custom models

This commit is contained in:
2026-06-10 12:09:44 +10:00
parent 264d28a965
commit 5a42ad9e37
3 changed files with 29 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
import Anthropic from '@anthropic-ai/sdk'
import { getApiKey } from './globalConfig'
import { getApiKey, getAIConfig } from './globalConfig'
import type { Market } from './fileSystem'
export type AnalysisMode = 'compression' | 'ending' | 'tone' | 'market_fit' | 'chat'
@@ -17,6 +17,9 @@ export interface AIPayload {
userMessage: string
}
const DEFAULT_MODEL = 'claude-sonnet-4-6'
const DEFAULT_PROMPT_MODEL = 'claude-haiku-4-5-20251001'
let _client: Anthropic | null = null
export function resetClient(): void {
@@ -29,7 +32,8 @@ function getClient(): Anthropic {
if (!apiKey || apiKey === 'your-api-key-here') {
throw new Error('API key not set. Open Borges → Preferences to configure it.')
}
_client = new Anthropic({ apiKey })
const { baseURL } = getAIConfig()
_client = new Anthropic({ apiKey, ...(baseURL ? { baseURL } : {}) })
}
return _client
}
@@ -97,8 +101,9 @@ Be candid. If the fit is poor, say so plainly.`
export async function streamPrompt(onChunk: (chunk: string) => void): Promise<void> {
const client = getClient()
const { promptModel } = getAIConfig()
const stream = client.messages.stream({
model: 'claude-haiku-4-5-20251001',
model: promptModel ?? DEFAULT_PROMPT_MODEL,
max_tokens: 120,
messages: [{
role: 'user',
@@ -118,6 +123,7 @@ export async function streamMessage(
onChunk: (chunk: string) => void
): Promise<void> {
const client = getClient()
const { model } = getAIConfig()
const messages: Anthropic.MessageParam[] = [
...payload.conversationHistory.slice(-10),
@@ -125,7 +131,7 @@ export async function streamMessage(
]
const stream = client.messages.stream({
model: 'claude-sonnet-4-6',
model: model ?? DEFAULT_MODEL,
max_tokens: 4096,
system: buildSystemPrompt(payload),
messages

View File

@@ -2,31 +2,43 @@ import { join } from 'path'
import { homedir } from 'os'
import { readFileSync, writeFileSync, mkdirSync } from 'fs'
export interface AIProviderConfig {
baseURL?: string
model?: string
promptModel?: string
}
export interface GlobalConfig {
apiKey?: string
collectionPath?: string
fontSize?: number
theme?: 'dark' | 'light'
defaultWordCountTarget?: number
ai?: AIProviderConfig
}
const CONFIG_DIR = join(homedir(), '.borges')
const CONFIG_FILE = join(CONFIG_DIR, 'config.json')
let _config: GlobalConfig = {}
try {
_config = JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'))
} catch {
// first run
function loadFromDisk(): GlobalConfig {
try {
return JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'))
} catch {
return {}
}
}
let _config: GlobalConfig = loadFromDisk()
export const getCollectionRoot = (): string =>
_config.collectionPath ?? join(homedir(), 'Documents', 'borges-collection')
export const getApiKey = (): string | undefined =>
_config.apiKey ?? process.env.ANTHROPIC_API_KEY
export const getAIConfig = (): AIProviderConfig =>
_config.ai ?? {}
export function readGlobalConfig(): GlobalConfig {
return { ..._config }
}

View File

@@ -57,7 +57,7 @@ export function registerIpcHandlers(): void {
ipcMain.handle('config:read', async () => readGlobalConfig())
ipcMain.handle('config:write', async (_e, updates: Partial<GlobalConfig>) => {
writeGlobalConfig(updates)
if (updates.apiKey !== undefined) resetClient()
resetClient() // always reset — baseURL or model may have changed
})
ipcMain.handle('config:pickFolder', async (event): Promise<string | null> => {
const win = BrowserWindow.fromWebContents(event.sender)