mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
feat(copilot): stats tracking (#1227)
* Add copilot stats table schema * Move db to agent * Lint * Fix tests
This commit is contained in:
committed by
GitHub
parent
1a5d5ddffa
commit
c2d668c3eb
@@ -224,6 +224,7 @@ describe('Copilot Chat API Route', () => {
|
||||
stream: true,
|
||||
streamToolCalls: true,
|
||||
mode: 'agent',
|
||||
messageId: 'mock-uuid-1234-5678',
|
||||
depth: 0,
|
||||
}),
|
||||
})
|
||||
@@ -286,6 +287,7 @@ describe('Copilot Chat API Route', () => {
|
||||
stream: true,
|
||||
streamToolCalls: true,
|
||||
mode: 'agent',
|
||||
messageId: 'mock-uuid-1234-5678',
|
||||
depth: 0,
|
||||
}),
|
||||
})
|
||||
@@ -337,6 +339,7 @@ describe('Copilot Chat API Route', () => {
|
||||
stream: true,
|
||||
streamToolCalls: true,
|
||||
mode: 'agent',
|
||||
messageId: 'mock-uuid-1234-5678',
|
||||
depth: 0,
|
||||
}),
|
||||
})
|
||||
@@ -425,6 +428,7 @@ describe('Copilot Chat API Route', () => {
|
||||
stream: true,
|
||||
streamToolCalls: true,
|
||||
mode: 'ask',
|
||||
messageId: 'mock-uuid-1234-5678',
|
||||
depth: 0,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -108,6 +108,8 @@ export async function POST(req: NextRequest) {
|
||||
conversationId,
|
||||
contexts,
|
||||
} = ChatMessageSchema.parse(body)
|
||||
// Ensure we have a consistent user message ID for this request
|
||||
const userMessageIdToUse = userMessageId || crypto.randomUUID()
|
||||
try {
|
||||
logger.info(`[${tracker.requestId}] Received chat POST`, {
|
||||
hasContexts: Array.isArray(contexts),
|
||||
@@ -369,6 +371,7 @@ export async function POST(req: NextRequest) {
|
||||
stream: stream,
|
||||
streamToolCalls: true,
|
||||
mode: mode,
|
||||
messageId: userMessageIdToUse,
|
||||
...(providerConfig ? { provider: providerConfig } : {}),
|
||||
...(effectiveConversationId ? { conversationId: effectiveConversationId } : {}),
|
||||
...(typeof effectiveDepth === 'number' ? { depth: effectiveDepth } : {}),
|
||||
@@ -414,7 +417,7 @@ export async function POST(req: NextRequest) {
|
||||
if (stream && simAgentResponse.body) {
|
||||
// Create user message to save
|
||||
const userMessage = {
|
||||
id: userMessageId || crypto.randomUUID(), // Use frontend ID if provided
|
||||
id: userMessageIdToUse, // Consistent ID used for request and persistence
|
||||
role: 'user',
|
||||
content: message,
|
||||
timestamp: new Date().toISOString(),
|
||||
@@ -810,7 +813,7 @@ export async function POST(req: NextRequest) {
|
||||
// Save messages if we have a chat
|
||||
if (currentChat && responseData.content) {
|
||||
const userMessage = {
|
||||
id: userMessageId || crypto.randomUUID(), // Use frontend ID if provided
|
||||
id: userMessageIdToUse, // Consistent ID used for request and persistence
|
||||
role: 'user',
|
||||
content: message,
|
||||
timestamp: new Date().toISOString(),
|
||||
|
||||
80
apps/sim/app/api/copilot/stats/route.ts
Normal file
80
apps/sim/app/api/copilot/stats/route.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
authenticateCopilotRequestSessionOnly,
|
||||
createBadRequestResponse,
|
||||
createInternalServerErrorResponse,
|
||||
createRequestTracker,
|
||||
createUnauthorizedResponse,
|
||||
} from '@/lib/copilot/auth'
|
||||
import { env } from '@/lib/env'
|
||||
import { SIM_AGENT_API_URL_DEFAULT } from '@/lib/sim-agent'
|
||||
|
||||
const SIM_AGENT_API_URL = env.SIM_AGENT_API_URL || SIM_AGENT_API_URL_DEFAULT
|
||||
|
||||
const BodySchema = z
|
||||
.object({
|
||||
// Do NOT send id; messageId is the unique correlator
|
||||
userId: z.string().optional(),
|
||||
chatId: z.string().uuid().optional(),
|
||||
messageId: z.string().optional(),
|
||||
depth: z.number().int().nullable().optional(),
|
||||
maxEnabled: z.boolean().nullable().optional(),
|
||||
createdAt: z.union([z.string().datetime(), z.date()]).optional(),
|
||||
diffCreated: z.boolean().nullable().optional(),
|
||||
diffAccepted: z.boolean().nullable().optional(),
|
||||
duration: z.number().int().nullable().optional(),
|
||||
inputTokens: z.number().int().nullable().optional(),
|
||||
outputTokens: z.number().int().nullable().optional(),
|
||||
aborted: z.boolean().nullable().optional(),
|
||||
})
|
||||
.passthrough()
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const tracker = createRequestTracker()
|
||||
try {
|
||||
const { userId, isAuthenticated } = await authenticateCopilotRequestSessionOnly()
|
||||
if (!isAuthenticated || !userId) {
|
||||
return createUnauthorizedResponse()
|
||||
}
|
||||
|
||||
const json = await req.json().catch(() => ({}))
|
||||
const parsed = BodySchema.safeParse(json)
|
||||
if (!parsed.success) {
|
||||
return createBadRequestResponse('Invalid request body for copilot stats')
|
||||
}
|
||||
const body = parsed.data as any
|
||||
|
||||
// Build outgoing payload for Sim Agent; do not include id
|
||||
const payload: Record<string, any> = {
|
||||
...body,
|
||||
userId: body.userId || userId,
|
||||
createdAt: body.createdAt || new Date().toISOString(),
|
||||
}
|
||||
payload.id = undefined
|
||||
|
||||
const agentRes = await fetch(`${SIM_AGENT_API_URL}/api/stats`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(env.COPILOT_API_KEY ? { 'x-api-key': env.COPILOT_API_KEY } : {}),
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
// Prefer not to block clients; still relay status
|
||||
let agentJson: any = null
|
||||
try {
|
||||
agentJson = await agentRes.json()
|
||||
} catch {}
|
||||
|
||||
if (!agentRes.ok) {
|
||||
const message = (agentJson && (agentJson.error || agentJson.message)) || 'Upstream error'
|
||||
return NextResponse.json({ success: false, error: message }, { status: 400 })
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true })
|
||||
} catch (error) {
|
||||
return createInternalServerErrorResponse('Failed to forward copilot stats')
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user