Skip to content

Commit 5db23dc

Browse files
h0842398z423gLeonFraschWunderwaffel
authored
Feature/frontend pdfchat (#47)
* backend implementation of the ai chat feature with (frontend) updated openapi generator, pinia store for chats and service to run chat * fix provider: cast input messages * rerun openapi generator and resolved lint error * unify paper and chat domains and regenerate api client * removed old files (chat dto, routes, services) and updated papers.ts * lint * feat(frontend): Add Chat with PDF side panel - Add ChatMessage.vue atom for rendering user/AI messages - Add ChatPanel.vue resizable side panel with chat interface - Integrate chat panel into PdfViewerDialog with toggle button - Fix import path in chat store (paperService -> papers) * Improve chat UI: better contrast, maximize button, theme toggle * Update OpenAI provider --------- Co-authored-by: ge87pot <ge87pot@tum.de> Co-authored-by: Wunderwaffel <37752072+Wunderwaffel@users.noreply.github.com>
1 parent 4fddc99 commit 5db23dc

17 files changed

Lines changed: 593 additions & 94 deletions

File tree

backend/app/llm/openai/provider.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ async def extract_keywords(self, user_text: str) -> List[str]:
3737
reasoning={"effort": "low"},
3838
input=[
3939
{
40-
"role": "developer",
40+
"role": "system",
4141
"content": KEYWORD_PROMPT,
4242
},
4343
{
@@ -55,9 +55,9 @@ async def extract_keywords(self, user_text: str) -> List[str]:
5555
return keyword_list
5656

5757
async def extract_keywords_from_pdf(
58-
self,
59-
pdf_text: str,
60-
query: Optional[str] = None,
58+
self,
59+
pdf_text: str,
60+
query: Optional[str] = None,
6161
) -> List[str]:
6262
"""
6363
Extracts a list of search queries from the full text of a paper and an optional query.
@@ -72,7 +72,7 @@ async def extract_keywords_from_pdf(
7272
reasoning={"effort": "medium"},
7373
input=[
7474
{
75-
"role": "developer",
75+
"role": "system",
7676
"content": PDF_KEYWORD_PROMPT,
7777
},
7878
{
@@ -131,7 +131,7 @@ async def summarise_paper(self, paper_text: str, query: str) -> Dict[str, Any]:
131131
reasoning={"effort": "medium"},
132132
input=[
133133
{
134-
"role": "developer",
134+
"role": "system",
135135
"content": prompt_content,
136136
},
137137
{
@@ -165,7 +165,7 @@ async def summarise_paper(self, paper_text: str, query: str) -> Dict[str, Any]:
165165
return data
166166

167167
async def chat_about_paper(
168-
self, paper_text: str, user_query: str, chat_history: List[Dict[str, str]]
168+
self, paper_text: str, user_query: str, chat_history: List[Dict[str, str]]
169169
) -> str:
170170
"""
171171
Handles a chat turn using the full paper text as context.

backend/app/routes/paper_routes.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
from app.schemas.paper_dto import (
1010
PaperChatRequest,
1111
PaperChatResponse,
12+
PaperSummaryRequest,
13+
PaperSummaryResponse,
1214
)
13-
from app.schemas.paper_dto import PaperSummaryRequest, PaperSummaryResponse
1415
from app.services.paper_service import PaperService
1516

1617
logger = logging.getLogger("inquiro")
@@ -42,7 +43,7 @@ async def summary(
4243
summary="Chat with the specified paper",
4344
)
4445
async def chat_with_paper(
45-
paper_id: int, request: PaperChatRequest, db: AsyncSession = Depends(get_db)
46+
paper_id: int, request: PaperChatRequest, db: AsyncSession = Depends(get_db)
4647
) -> PaperChatResponse:
4748
"""
4849
Allows the user to ask questions about the paper currently being viewed.

frontend/src/api/.openapi-generator/FILES

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ models/condition-group-children-inner.ts
1414
models/condition-group.ts
1515
models/httpvalidation-error.ts
1616
models/index.ts
17+
models/location-inner.ts
1718
models/login-request.ts
1819
models/login-response.ts
1920
models/paper-chat-request.ts
@@ -32,5 +33,4 @@ models/search-response.ts
3233
models/text-condition.ts
3334
models/user-create.ts
3435
models/user-response.ts
35-
models/validation-error-loc-inner.ts
3636
models/validation-error.ts
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7.18.0-SNAPSHOT
1+
7.19.0

frontend/src/api/apis/authentication-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
1818
import globalAxios from 'axios';
1919
// Some imports not used depending on template conditions
2020
// @ts-ignore
21-
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common';
21+
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from '../common';
2222
// @ts-ignore
2323
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
2424
// @ts-ignore

frontend/src/api/apis/paper-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
1818
import globalAxios from 'axios';
1919
// Some imports not used depending on template conditions
2020
// @ts-ignore
21-
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common';
21+
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from '../common';
2222
// @ts-ignore
2323
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
2424
// @ts-ignore

frontend/src/api/apis/projects-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
1818
import globalAxios from 'axios';
1919
// Some imports not used depending on template conditions
2020
// @ts-ignore
21-
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common';
21+
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from '../common';
2222
// @ts-ignore
2323
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
2424
// @ts-ignore

frontend/src/api/apis/search-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
1818
import globalAxios from 'axios';
1919
// Some imports not used depending on template conditions
2020
// @ts-ignore
21-
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common';
21+
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from '../common';
2222
// @ts-ignore
2323
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
2424
// @ts-ignore

frontend/src/api/apis/users-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
1818
import globalAxios from 'axios';
1919
// Some imports not used depending on template conditions
2020
// @ts-ignore
21-
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common';
21+
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction, replaceWithSerializableTypeIfNeeded } from '../common';
2222
// @ts-ignore
2323
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
2424
// @ts-ignore

frontend/src/api/common.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export const setOAuthToObject = async function (object: any, name: string, scope
6666
function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void {
6767
if (parameter == null) return;
6868
if (typeof parameter === "object") {
69-
if (Array.isArray(parameter)) {
69+
if (Array.isArray(parameter) || parameter instanceof Set) {
7070
(parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key));
7171
}
7272
else {
@@ -91,13 +91,26 @@ export const setSearchParams = function (url: URL, ...objects: any[]) {
9191
url.search = searchParams.toString();
9292
}
9393

94+
/**
95+
* JSON serialization helper function which replaces instances of unserializable types with serializable ones.
96+
* This function will run for every key-value pair encountered by JSON.stringify while traversing an object.
97+
* Converting a set to a string will return an empty object, so an intermediate conversion to an array is required.
98+
*/
99+
export const replaceWithSerializableTypeIfNeeded = function(key: any, value: any) {
100+
if (value instanceof Set) {
101+
return Array.from(value);
102+
} else {
103+
return value;
104+
}
105+
}
106+
94107
export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) {
95108
const nonString = typeof value !== 'string';
96109
const needsSerialization = nonString && configuration && configuration.isJsonMime
97110
? configuration.isJsonMime(requestOptions.headers['Content-Type'])
98111
: nonString;
99112
return needsSerialization
100-
? JSON.stringify(value !== undefined ? value : {})
113+
? JSON.stringify(value !== undefined ? value : {}, replaceWithSerializableTypeIfNeeded)
101114
: (value || "");
102115
}
103116

0 commit comments

Comments
 (0)