-
-
Notifications
You must be signed in to change notification settings - Fork 208
Expand file tree
/
Copy pathapi.reports.ts
More file actions
121 lines (107 loc) · 3.85 KB
/
api.reports.ts
File metadata and controls
121 lines (107 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { createFileRoute } from '@tanstack/react-router'
import { chat, maxIterations, toServerSentEventsStream } from '@tanstack/ai'
import { createCodeMode } from '@tanstack/ai-code-mode'
import { anthropicText } from '@tanstack/ai-anthropic'
import { openaiText } from '@tanstack/ai-openai'
import { geminiText } from '@tanstack/ai-gemini'
import { zaiText } from '@tanstack/ai-zai'
import type { AnyTextAdapter } from '@tanstack/ai'
import { allTools } from '@/lib/tools'
import { CODE_MODE_SYSTEM_PROMPT, REPORTS_SYSTEM_PROMPT } from '@/lib/prompts'
import { reportTools } from '@/lib/reports/tools'
import { createReportBindings } from '@/lib/reports/create-report-bindings'
type Provider = 'anthropic' | 'openai' | 'gemini' | 'zai'
function getAdapter(provider: Provider, model?: string): AnyTextAdapter {
switch (provider) {
case 'openai':
return openaiText((model || 'gpt-4o') as 'gpt-4o')
case 'gemini':
return geminiText((model || 'gemini-2.5-flash') as 'gemini-2.5-flash')
case 'zai':
return zaiText((model || 'glm-4.7') as 'glm-4.7')
case 'anthropic':
default:
return anthropicText((model || 'claude-haiku-4-5') as 'claude-haiku-4-5')
}
}
// Lazy initialization to avoid loading native modules at module load time
// This is necessary for RSC compatibility with Vite's module runner
let codeModeCache: {
tool: ReturnType<typeof createCodeMode>['tool']
systemPrompt: string
} | null = null
async function getCodeModeTools() {
if (!codeModeCache) {
const { createIsolateDriver } = await import('@/lib/create-isolate-driver')
const driver = await createIsolateDriver('node')
const { tool, systemPrompt } = createCodeMode({
driver,
tools: allTools,
timeout: 60000,
memoryLimit: 128,
getSkillBindings: async () => createReportBindings(),
})
codeModeCache = { tool, systemPrompt }
}
return codeModeCache
}
export const Route = createFileRoute('/_reporting/api/reports' as any)({
server: {
handlers: {
POST: async ({ request }) => {
const requestSignal = request.signal
if (requestSignal.aborted) {
return new Response(null, { status: 499 })
}
const abortController = new AbortController()
const body = await request.json()
const { messages, data } = body
const provider: Provider = data?.provider || 'anthropic'
const model: string | undefined = data?.model
const adapter = getAdapter(provider, model)
const { tool, systemPrompt } = await getCodeModeTools()
try {
const stream = chat({
adapter,
messages,
tools: [tool, ...reportTools],
systemPrompts: [
CODE_MODE_SYSTEM_PROMPT,
systemPrompt,
REPORTS_SYSTEM_PROMPT,
],
agentLoopStrategy: maxIterations(20),
abortController,
maxTokens: 8192,
})
const sseStream = toServerSentEventsStream(stream, abortController)
return new Response(sseStream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
},
})
} catch (error: unknown) {
console.error('[API Reports Route] Error:', error)
if (
(error instanceof Error && error.name === 'AbortError') ||
abortController.signal.aborted
) {
return new Response(null, { status: 499 })
}
return new Response(
JSON.stringify({
error:
error instanceof Error ? error.message : 'An error occurred',
}),
{
status: 500,
headers: { 'Content-Type': 'application/json' },
},
)
}
},
},
},
})