Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion web-app/src/lib/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
ConfigOptions,
} from 'token.js'

import { getModelCapabilities } from '@/lib/models'

// Extended config options to include custom fetch function
type ExtendedConfigOptions = ConfigOptions & {
fetch?: typeof fetch
Expand All @@ -38,6 +40,7 @@
import { useAppState } from '@/hooks/useAppState'
import { injectFilesIntoPrompt } from './fileMetadata'
import { Attachment } from '@/types/attachment'
import { ModelCapabilities } from '@/types/models'

export type ChatCompletionResponse =
| chatCompletion
Expand Down Expand Up @@ -66,11 +69,11 @@
const docMetadata = documents
.filter((doc) => doc.id) // Only include processed documents
.map((doc) => ({
id: doc.id!,
name: doc.name,
type: doc.fileType,
size: typeof doc.size === 'number' ? doc.size : undefined,
chunkCount: typeof doc.chunkCount === 'number' ? doc.chunkCount : undefined,

Check warning on line 76 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

72-76 lines are not covered with tests
}))

const textWithFiles =
Expand All @@ -88,15 +91,15 @@

// Add image attachments to content array
images.forEach((img) => {
if (img.base64 && img.mimeType) {
contentParts.push({
type: ContentType.Image,
image_url: {
url: `data:${img.mimeType};base64,${img.base64}`,
detail: 'auto',
},
} as any)
}

Check warning on line 102 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

94-102 lines are not covered with tests
})

return {
Expand Down Expand Up @@ -168,45 +171,45 @@
* @returns
*/
export const sendCompletion = async (
thread: Thread,
provider: ModelProvider,
messages: ChatCompletionMessageParam[],
abortController: AbortController,
tools: MCPTool[] = [],
stream: boolean = true,
params: Record<string, object> = {}
): Promise<ChatCompletionResponse | undefined> => {
if (!thread?.model?.id || !provider) return undefined

Check warning on line 182 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

174-182 lines are not covered with tests

let providerName = provider.provider as unknown as keyof typeof models

Check warning on line 184 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

184 line is not covered with tests

if (!Object.keys(models).some((key) => key === providerName))
providerName = 'openai-compatible'

Check warning on line 187 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

186-187 lines are not covered with tests

const tokenJS = new TokenJS({
apiKey:
provider.api_key ?? (await getServiceHub().core().getAppToken()) ?? '',

Check warning on line 191 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

189-191 lines are not covered with tests
// TODO: Retrieve from extension settings
baseURL: provider.base_url,

Check warning on line 193 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

193 line is not covered with tests
// Use Tauri's fetch to avoid CORS issues only for openai-compatible provider
fetch: IS_DEV ? fetch : getServiceHub().providers().fetch(),

Check warning on line 195 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

195 line is not covered with tests
// OpenRouter identification headers for Jan
// ref: https://openrouter.ai/docs/api-reference/overview#headers
...(provider.provider === 'openrouter' && {
defaultHeaders: {
'HTTP-Referer': 'https://jan.ai',
'X-Title': 'Jan',
},
}),

Check warning on line 203 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

198-203 lines are not covered with tests
// Add Origin header for local providers to avoid CORS issues
...((provider.base_url?.includes('localhost:') ||
provider.base_url?.includes('127.0.0.1:')) && {
fetch: getServiceHub().providers().fetch(),
defaultHeaders: {
Origin: 'tauri://localhost',
},
}),
} as ExtendedConfigOptions)

Check warning on line 212 in web-app/src/lib/completion.ts

View workflow job for this annotation

GitHub Actions / coverage-check

205-212 lines are not covered with tests

if (
thread.model.id &&
Expand All @@ -232,10 +235,25 @@
}

// Inject RAG tools on-demand (not in global tools list)
const providerModelConfig = provider.models?.find(
(model) => model.id === thread.model?.id || model.model === thread.model?.id
)
const effectiveCapabilities = Array.isArray(
providerModelConfig?.capabilities
)
? providerModelConfig?.capabilities ?? []
: getModelCapabilities(provider.provider, thread.model.id)
const modelSupportsTools = effectiveCapabilities.includes(
ModelCapabilities.TOOLS
)
let usableTools = tools
try {
const attachmentsEnabled = useAttachments.getState().enabled
if (attachmentsEnabled && PlatformFeatures[PlatformFeature.ATTACHMENTS]) {
if (
attachmentsEnabled &&
PlatformFeatures[PlatformFeature.ATTACHMENTS] &&
modelSupportsTools
) {
const ragTools = await getServiceHub().rag().getTools().catch(() => [])
if (Array.isArray(ragTools) && ragTools.length) {
usableTools = [...tools, ...ragTools]
Expand Down
Loading