Skip to content

Commit 48f64f9

Browse files
committed
feat: Adjusting types & request for handling the files WIP
1 parent 833118e commit 48f64f9

File tree

4 files changed

+171
-99
lines changed

4 files changed

+171
-99
lines changed

tools/server/webui/src/lib/services/chat.ts

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
import type { Message } from '$lib/types/database';
2-
3-
export interface LlamaCppServerProps {
4-
build_info: string;
5-
model_path: string;
6-
n_ctx: number;
7-
modalities?: {
8-
vision: boolean;
9-
audio: boolean;
10-
};
11-
}
1+
import type {
2+
ApiChatMessageContentPart,
3+
ApiChatMessageData,
4+
ApiChatCompletionRequest,
5+
ApiChatCompletionStreamChunk,
6+
ApiChatCompletionResponse,
7+
ApiLlamaCppServerProps
8+
} from '$lib/types/api';
9+
10+
import type {
11+
DatabaseMessage,
12+
DatabaseMessageExtra,
13+
DatabaseMessageExtraImageFile,
14+
DatabaseMessageExtraTextFile
15+
} from '$lib/types/database'
1216

1317
export class ChatService {
1418
private baseUrl: string;
@@ -19,7 +23,7 @@ export class ChatService {
1923
}
2024

2125
async sendMessage(
22-
messages: ChatMessageData[],
26+
messages: ApiChatMessageData[],
2327
options: {
2428
stream?: boolean;
2529
temperature?: number;
@@ -35,7 +39,7 @@ export class ChatService {
3539
this.abort();
3640
this.abortController = new AbortController();
3741

38-
const requestBody: ChatCompletionRequest = {
42+
const requestBody: ApiChatCompletionRequest = {
3943
messages: messages.map((msg) => ({
4044
role: msg.role,
4145
content: msg.content
@@ -119,7 +123,7 @@ export class ChatService {
119123
}
120124

121125
try {
122-
const parsed: ChatCompletionStreamChunk = JSON.parse(data);
126+
const parsed: ApiChatCompletionStreamChunk = JSON.parse(data);
123127
const content = parsed.choices[0]?.delta?.content;
124128
if (content) {
125129
fullResponse += content;
@@ -161,7 +165,7 @@ export class ChatService {
161165
onError?: (error: Error) => void
162166
): Promise<string> {
163167
try {
164-
const data: ChatCompletionResponse = await response.json();
168+
const data: ApiChatCompletionResponse = await response.json();
165169
const content = data.choices[0]?.message?.content || '';
166170

167171
onComplete?.(content);
@@ -177,10 +181,57 @@ export class ChatService {
177181
}
178182

179183
/**
180-
* Unified method to send chat completions supporting both ChatMessageData and Message types
184+
* Convert Message with extras to ApiChatMessageData format for API requests
185+
*/
186+
private static convertMessageToChatData(message: DatabaseMessage & { extra?: DatabaseMessageExtra[] }): ApiChatMessageData {
187+
// If no extras, return simple text message
188+
if (!message.extra || message.extra.length === 0) {
189+
return {
190+
role: message.role as 'user' | 'assistant' | 'system',
191+
content: message.content
192+
};
193+
}
194+
195+
// Build multimodal content array
196+
const contentParts: ApiChatMessageContentPart[] = [];
197+
198+
// Add text content first
199+
if (message.content) {
200+
contentParts.push({
201+
type: 'text',
202+
text: message.content
203+
});
204+
}
205+
206+
// Add image files
207+
const imageFiles = message.extra.filter((extra): extra is DatabaseMessageExtraImageFile => extra.type === 'imageFile');
208+
imageFiles.forEach((image) => {
209+
contentParts.push({
210+
type: 'image_url',
211+
image_url: { url: image.base64Url }
212+
});
213+
});
214+
215+
// Add text files as additional text content
216+
const textFiles = message.extra.filter((extra): extra is DatabaseMessageExtraTextFile => extra.type === 'textFile');
217+
textFiles.forEach((textFile) => {
218+
contentParts.push({
219+
type: 'text',
220+
text: `\n\n--- File: ${textFile.name} ---\n${textFile.content}`
221+
});
222+
});
223+
224+
return {
225+
role: message.role as 'user' | 'assistant' | 'system',
226+
content: contentParts
227+
};
228+
}
229+
230+
/**
231+
* Unified method to send chat completions supporting both ApiChatMessageData and Message types
181232
*/
182233
async sendChatCompletion(
183-
messages: ChatMessageData[] | Message[],
234+
messages: (ApiChatMessageData[] | DatabaseMessage[]) | (DatabaseMessage & { extra?: DatabaseMessageExtra[] })[],
184235
options: {
185236
stream?: boolean;
186237
temperature?: number;
@@ -190,12 +241,16 @@ export class ChatService {
190241
onError?: (error: Error) => void;
191242
} = {}
192243
): Promise<string | void> {
193-
// Normalize messages to ChatMessageData format
194-
const normalizedMessages: ChatMessageData[] = messages.map((msg) => ({
195-
role: msg.role as ChatMessageData['role'],
196-
content: msg.content,
197-
timestamp: 'timestamp' in msg ? msg.timestamp : undefined
198-
}));
244+
// Handle both array formats and convert messages with extras
245+
const normalizedMessages: ApiChatMessageData[] = messages.map((msg) => {
246+
// Check if this is already a ApiChatMessageData object
247+
if ('content' in msg && (typeof msg.content === 'string' || Array.isArray(msg.content))) {
248+
return msg as ApiChatMessageData;
249+
}
250+
251+
// Convert DatabaseMessage with extras to ApiChatMessageData
252+
return ChatService.convertMessageToChatData(msg as DatabaseMessage & { extra?: DatabaseMessageExtra[] });
253+
});
199254

200255
// Set default options for API compatibility
201256
const finalOptions = {
@@ -212,7 +267,7 @@ export class ChatService {
212267
* Static method for backward compatibility with ApiService
213268
*/
214269
static async sendChatCompletion(
215-
messages: Message[],
270+
messages: DatabaseMessage[],
216271
onChunk?: (content: string) => void,
217272
onComplete?: () => void,
218273
onError?: (error: Error) => void
@@ -232,7 +287,7 @@ export class ChatService {
232287
/**
233288
* Get server properties - static method for API compatibility
234289
*/
235-
static async getServerProps(): Promise<LlamaCppServerProps> {
290+
static async getServerProps(): Promise<ApiLlamaCppServerProps> {
236291
try {
237292
const response = await fetch(`${import.meta.env.VITE_BASE_URL}/props`, {
238293
headers: {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
export interface ApiChatMessageContentPart {
2+
type: 'text' | 'image_url';
3+
text?: string;
4+
image_url?: {
5+
url: string;
6+
};
7+
}
8+
9+
export interface ApiChatMessageData {
10+
role: ChatRole;
11+
content: string | ApiChatMessageContentPart[];
12+
timestamp?: number;
13+
}
14+
15+
export interface ApiLlamaCppServerProps {
16+
build_info: string;
17+
model_path: string;
18+
n_ctx: number;
19+
modalities?: {
20+
vision: boolean;
21+
audio: boolean;
22+
};
23+
}
24+
25+
export interface ApiChatCompletionRequest {
26+
messages: Array<{
27+
role: ChatRole;
28+
content: string | ApiChatMessageContentPart[];
29+
}>;
30+
stream?: boolean;
31+
temperature?: number;
32+
max_tokens?: number;
33+
}
34+
35+
export interface ApiChatCompletionStreamChunk {
36+
choices: Array<{
37+
delta: {
38+
content?: string;
39+
};
40+
}>;
41+
timings?: {
42+
prompt_n?: number;
43+
prompt_ms?: number;
44+
predicted_n?: number;
45+
predicted_ms?: number;
46+
};
47+
}
48+
49+
export interface ApiChatCompletionResponse {
50+
choices: Array<{
51+
message: {
52+
content: string;
53+
};
54+
}>;
55+
}
Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,2 @@
1-
export type ChatRole = 'user' | 'assistant' | 'system';
2-
3-
// todo - currently we're processing <think>...</think> tags from the streamed 'text' response. Should this be done differently?
41
export type ChatMessageType = 'root' | 'text' | 'think';
5-
6-
export interface ChatMessageData {
7-
role: ChatRole;
8-
type: ChatMessageType
9-
content: string;
10-
thinking?: string; // Content between <think> and </think> tags
11-
timestamp?: Date | number;
12-
}
13-
14-
export interface ChatCompletionRequest {
15-
messages: Array<{ role: string; content: string }>;
16-
stream?: boolean;
17-
temperature?: number;
18-
max_tokens?: number;
19-
top_p?: number;
20-
frequency_penalty?: number;
21-
presence_penalty?: number;
22-
}
23-
24-
export interface ChatCompletionResponse {
25-
id: string;
26-
object: string;
27-
created: number;
28-
model: string;
29-
choices: Array<{
30-
index: number;
31-
message: {
32-
role: string;
33-
content: string;
34-
};
35-
finish_reason: string;
36-
}>;
37-
usage?: {
38-
prompt_tokens: number;
39-
completion_tokens: number;
40-
total_tokens: number;
41-
};
42-
}
43-
44-
export interface ChatCompletionStreamChunk {
45-
id: string;
46-
object: string;
47-
created: number;
48-
model: string;
49-
choices: Array<{
50-
index: number;
51-
delta: {
52-
role?: string;
53-
content?: string;
54-
};
55-
finish_reason?: string;
56-
}>;
57-
}
2+
export type ChatRole = 'user' | 'assistant' | 'system';
Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,48 @@
1-
export interface Conversation {
1+
export interface DatabaseAppSettings {
2+
id: string;
3+
theme: 'light' | 'dark' | 'system';
4+
model: string;
5+
temperature: number;
6+
}
7+
8+
9+
export interface DatabaseConversation {
210
currNode: string | null;
311
id: string;
412
lastModified: number;
513
name: string;
614
}
715

8-
export interface Message {
16+
export interface DatabaseMessageExtraAudioFile {
17+
type: 'audioFile';
18+
name: string;
19+
base64Data: string;
20+
mimeType: string;
21+
}
22+
23+
export interface DatabaseMessageExtraImageFile {
24+
type: 'imageFile';
25+
name: string;
26+
base64Url: string;
27+
}
28+
29+
export interface DatabaseMessageExtraTextFile {
30+
type: 'textFile';
31+
name: string;
32+
content: string;
33+
}
34+
35+
export type DatabaseMessageExtra = DatabaseMessageExtraImageFile | DatabaseMessageExtraTextFile | DatabaseMessageExtraAudioFile;
36+
37+
export interface DatabaseMessage {
938
id: string;
1039
convId: string;
11-
type: 'root' | 'text' | 'think';
40+
type: ChatMessageType;
1241
timestamp: number;
13-
role: 'system' | 'user' | 'assistant';
42+
role: ChatRole;
1443
content: string;
1544
parent: string;
1645
thinking: string;
1746
children: string[];
18-
}
19-
20-
export interface DatabaseAppSettings {
21-
id: string;
22-
theme: 'light' | 'dark' | 'system';
23-
model: string;
24-
temperature: number;
25-
maxTokens: number;
26-
topP: number;
27-
topK: number;
28-
repeatPenalty: number;
29-
seed: number;
30-
systemPrompt: string;
31-
}
47+
extra?: DatabaseMessageExtra[];
48+
}

0 commit comments

Comments
 (0)