openfox 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/branch.api-WDHKZJS6.js +48 -0
- package/dist/branch.api-WDHKZJS6.js.map +1 -0
- package/dist/chunk-26NRL3HX.js +116 -0
- package/dist/chunk-26NRL3HX.js.map +1 -0
- package/dist/chunk-43SWNGYW.js +273 -0
- package/dist/chunk-43SWNGYW.js.map +1 -0
- package/dist/chunk-4Z2OZ6NX.js +132 -0
- package/dist/chunk-4Z2OZ6NX.js.map +1 -0
- package/dist/chunk-6MOGZMGX.js +218 -0
- package/dist/chunk-6MOGZMGX.js.map +1 -0
- package/dist/chunk-AF7XS4OI.js +55 -0
- package/dist/chunk-AF7XS4OI.js.map +1 -0
- package/dist/chunk-EI46G5WO.js +138 -0
- package/dist/chunk-EI46G5WO.js.map +1 -0
- package/dist/chunk-FF6Z2MC3.js +844 -0
- package/dist/chunk-FF6Z2MC3.js.map +1 -0
- package/dist/chunk-IKAWIOXM.js +5817 -0
- package/dist/chunk-IKAWIOXM.js.map +1 -0
- package/dist/chunk-IVHCPQO2.js +29 -0
- package/dist/chunk-IVHCPQO2.js.map +1 -0
- package/dist/chunk-MTX5Y3V3.js +46 -0
- package/dist/chunk-MTX5Y3V3.js.map +1 -0
- package/dist/chunk-N2XYX4ZA.js +83 -0
- package/dist/chunk-N2XYX4ZA.js.map +1 -0
- package/dist/chunk-QY7BMXWT.js +124 -0
- package/dist/chunk-QY7BMXWT.js.map +1 -0
- package/dist/chunk-U62ZECSQ.js +54 -0
- package/dist/chunk-U62ZECSQ.js.map +1 -0
- package/dist/chunk-YQ36AK5A.js +5570 -0
- package/dist/chunk-YQ36AK5A.js.map +1 -0
- package/dist/cli/dev.d.ts +1 -0
- package/dist/cli/dev.js +14 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/command-defaults/defaults/commit-push.command.md +13 -0
- package/dist/command-defaults/defaults/init.command.md +18 -0
- package/dist/command-defaults/defaults/test-ui.command.md +16 -0
- package/dist/config-6GJ4SVFH.js +33 -0
- package/dist/config-6GJ4SVFH.js.map +1 -0
- package/dist/init-UVZDOHMX.js +185 -0
- package/dist/init-UVZDOHMX.js.map +1 -0
- package/dist/paths-4BPXWB2W.js +17 -0
- package/dist/paths-4BPXWB2W.js.map +1 -0
- package/dist/project-creator-CWPKRIKI.js +69 -0
- package/dist/project-creator-CWPKRIKI.js.map +1 -0
- package/dist/protocol-BJsm88OW.d.ts +760 -0
- package/dist/provider-LCE6WZA6.js +343 -0
- package/dist/provider-LCE6WZA6.js.map +1 -0
- package/dist/serve-27DZK34K.js +177 -0
- package/dist/serve-27DZK34K.js.map +1 -0
- package/dist/server/index.d.ts +479 -0
- package/dist/server/index.js +20 -0
- package/dist/server/index.js.map +1 -0
- package/dist/settings-D5O3TMLD.js +17 -0
- package/dist/settings-D5O3TMLD.js.map +1 -0
- package/dist/shared/index.d.ts +19 -0
- package/dist/shared/index.js +17 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/skill-defaults/defaults/playwright-cli.skill.md +38 -0
- package/dist/tools-R4YQAYUF.js +46 -0
- package/dist/tools-R4YQAYUF.js.map +1 -0
- package/dist/web/assets/html2canvas.esm-QH1iLAAe.js +22 -0
- package/dist/web/assets/index-BMNDCsbJ.js +120 -0
- package/dist/web/assets/index-BSlSWZrM.css +1 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-600-normal-8K4wrrwR.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-600-normal-EVf6-Yzo.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-600-normal-H7WoG9Et.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-600-normal-mc2nkWzM.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-600-normal-BfsvjouI.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-600-normal-C8RAYTDA.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-600-normal-BfB_LPfz.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-600-normal-DObL3zCW.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-600-normal-OWROknRo.woff +0 -0
- package/dist/web/assets/openfox.png +0 -0
- package/dist/web/fox.svg +25 -0
- package/dist/web/index.html +14 -0
- package/dist/web/sounds/achievement.mp3 +0 -0
- package/dist/web/sounds/intervention.mp3 +0 -0
- package/dist/web/sounds/notification.mp3 +0 -0
- package/dist/web/sounds/waiting-for-user.mp3 +0 -0
- package/package.json +101 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/shared/protocol.ts
|
|
2
|
+
function createClientMessage(type, payload) {
|
|
3
|
+
return {
|
|
4
|
+
id: crypto.randomUUID(),
|
|
5
|
+
type,
|
|
6
|
+
payload
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function createServerMessage(type, payload, correlationId) {
|
|
10
|
+
const message = { type, payload };
|
|
11
|
+
if (correlationId !== void 0) {
|
|
12
|
+
message.id = correlationId;
|
|
13
|
+
}
|
|
14
|
+
return message;
|
|
15
|
+
}
|
|
16
|
+
function isClientMessage(msg) {
|
|
17
|
+
return typeof msg === "object" && msg !== null && "id" in msg && "type" in msg && "payload" in msg;
|
|
18
|
+
}
|
|
19
|
+
function isServerMessage(msg) {
|
|
20
|
+
return typeof msg === "object" && msg !== null && "type" in msg && "payload" in msg;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
createClientMessage,
|
|
25
|
+
createServerMessage,
|
|
26
|
+
isClientMessage,
|
|
27
|
+
isServerMessage
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=chunk-IVHCPQO2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/protocol.ts"],"sourcesContent":["import type {\n Project,\n Session,\n SessionSummary,\n SessionMode,\n SessionPhase,\n ToolMode,\n Criterion,\n Message,\n Todo,\n Diagnostic,\n ToolResult,\n ContextState,\n Attachment,\n} from './types.js'\n\n// ============================================================================\n// Client → Server Messages\n// ============================================================================\n\nexport type ClientMessageType =\n // Project management\n | 'project.create'\n | 'project.create-with-dir'\n | 'project.list'\n | 'project.load'\n | 'project.update'\n | 'project.delete'\n // Session management\n | 'session.create'\n | 'session.load'\n | 'session.list'\n | 'session.delete'\n | 'session.deleteAll'\n // Unified chat (replaces plan.message, agent.start, etc.)\n | 'chat.send' // Send a message (works in any mode)\n | 'chat.stop' // Stop current generation\n | 'chat.continue' // Continue generation (after user interruption)\n // Mode switching\n | 'mode.switch' // Switch to a different mode\n | 'mode.accept' // Accept criteria and switch to builder (generates summary)\n // Criteria editing (from UI)\n | 'criteria.edit'\n // Context management\n | 'context.compact' // Manually trigger context compaction\n // Runner (auto-loop)\n | 'runner.launch' // Start the auto-loop runner (build → verify → done)\n // Path confirmation\n | 'path.confirm' // User response to path confirmation request\n // Settings management\n | 'settings.get' // Get a setting value\n | 'settings.set' // Set a setting value\n // Provider management\n | 'provider.activate' // Switch to a different provider\n | 'session.setProvider' // Set provider/model for current session\n // Message queue (while agent is running)\n | 'queue.asap' // Queue message for ASAP injection (between tool calls)\n | 'queue.completion' // Queue message for delivery on turn completion\n | 'queue.cancel' // Cancel a queued message\n\nexport interface ClientMessage<T = unknown> {\n id: string\n type: ClientMessageType\n payload: T\n}\n\n// Payload types for client messages\n\n// Project payloads\nexport interface ProjectCreatePayload {\n name: string\n workdir: string\n}\n\nexport interface ProjectCreateWithDirPayload {\n name: string\n}\n\nexport interface ProjectLoadPayload {\n projectId: string\n}\n\nexport interface ProjectUpdatePayload {\n projectId: string\n name?: string\n customInstructions?: string | null // null to clear\n}\n\nexport interface ProjectDeletePayload {\n projectId: string\n}\n\n// Session payloads\nexport interface SessionCreatePayload {\n projectId: string\n title?: string\n}\n\nexport interface SessionLoadPayload {\n sessionId: string\n lastEventSeq?: number // Resume from this sequence number (for reconnection)\n}\n\nexport interface SessionDeleteAllPayload {\n projectId: string\n}\n\n// Chat payloads (unified)\nexport interface ChatSendPayload {\n content: string\n attachments?: Attachment[]\n messageKind?: 'command'\n isSystemGenerated?: boolean\n}\n\nexport interface ModeSwitchPayload {\n mode: SessionMode\n}\n\n// Criteria payloads\nexport interface CriteriaEditPayload {\n criteria: Criterion[]\n}\n\n // Settings payloads\nexport interface SettingsGetPayload {\n key: string\n}\n\nexport interface SettingsSetPayload {\n key: string\n value: string\n}\n\nexport interface SettingsValuePayload {\n key: string\n value: string | null\n}\n\n// Provider payloads\nexport interface ProviderActivatePayload {\n providerId: string\n}\n\nexport interface SessionSetProviderPayload {\n providerId: string\n model?: string // If omitted, use provider's default model\n}\n\n// Queue payloads\nexport interface QueueAsapPayload {\n content: string\n attachments?: Attachment[]\n}\n\nexport interface QueueCompletionPayload {\n content: string\n attachments?: Attachment[]\n}\n\nexport interface QueueCancelPayload {\n queueId: string\n}\n\n// Shared queue types\nexport interface QueuedMessage {\n queueId: string\n mode: 'asap' | 'completion'\n content: string\n attachments?: Attachment[]\n queuedAt: string\n}\n\n// ============================================================================\n// Server → Client Messages\n// ============================================================================\n\nexport type ServerMessageType =\n // Project events\n | 'project.state'\n | 'project.list'\n | 'project.deleted'\n // Session events\n | 'session.state'\n | 'session.list'\n | 'session.deleted'\n | 'session.deletedAll'\n | 'session.running' // Real-time running state change\n | 'session.name_generated' // Session name was auto-generated\n // Unified chat events (replaces plan.delta, agent.event, etc.)\n | 'chat.delta' // Text streaming\n | 'chat.thinking' // Thinking block content\n | 'chat.tool_preparing' // Tool call detected, streaming arguments\n | 'chat.tool_call' // Tool being called\n | 'chat.tool_output' // Streaming tool output (stdout/stderr for run_command)\n | 'chat.tool_result' // Tool result\n | 'chat.todo' // Todo list update (displayed in chat)\n | 'chat.summary' // Summary block (displayed in chat)\n | 'chat.progress' // Progress update (e.g., \"Generating summary...\")\n | 'chat.format_retry' // Model used wrong format (XML tools), retrying\n | 'chat.message' // Full message added (system-generated, etc.)\n | 'chat.message_updated' // Message updated (e.g., isStreaming changed)\n | 'chat.done' // Current generation complete\n | 'chat.error' // Error during generation\n | 'chat.path_confirmation' // Request user confirmation for outside-workdir path access\n // Mode events\n | 'mode.changed' // Mode was changed\n // Phase events\n | 'phase.changed' // Workflow phase changed (plan/build/verification/done)\n // Task completion\n | 'task.completed' // Task finished with summary stats\n // Criteria events\n | 'criteria.updated' // Criteria changed\n // Context events\n | 'context.state' // Context window state update\n // Settings events\n | 'settings.value' // Setting value response\n // Provider events\n | 'provider.changed' // Active provider was switched\n // Message queue events\n | 'queue.state' // Broadcast current queue state to client\n // Other\n | 'lsp.diagnostics'\n | 'error'\n | 'ack'\n\nexport interface ServerMessage<T = unknown> {\n id?: string // Correlation ID if response to client message\n type: ServerMessageType\n payload: T\n seq?: number // Sequence number for event replay/subscription\n sessionId?: string // Session this event belongs to (for multi-session support)\n}\n\n// Payload types for server messages\n\n// Project payloads\nexport interface ProjectStatePayload {\n project: Project\n}\n\nexport interface ProjectListPayload {\n projects: Project[]\n}\n\nexport interface ProjectDeletedPayload {\n projectId: string\n}\n\n// Session payloads\nexport interface SessionStatePayload {\n session: Session\n messages: Message[] // All messages for this session\n}\n\nexport interface SessionListPayload {\n sessions: SessionSummary[]\n}\n\nexport interface SessionRunningPayload {\n isRunning: boolean\n}\n\nexport interface SessionNameGeneratedPayload {\n name: string\n}\n\n// Chat payloads (unified streaming)\n// All streaming payloads include messageId to identify which message to update\nexport interface ChatDeltaPayload {\n messageId: string\n content: string\n}\n\nexport interface ChatThinkingPayload {\n messageId: string\n content: string\n}\n\nexport interface ChatToolPreparingPayload {\n messageId: string\n index: number // Tool call index (for multiple parallel calls)\n name: string // Tool name (available early in stream)\n}\n\nexport interface ChatToolCallPayload {\n messageId: string\n callId: string\n tool: string\n args: Record<string, unknown>\n}\n\nexport interface ChatToolResultPayload {\n messageId: string\n callId: string\n tool: string\n result: ToolResult\n}\n\nexport interface ChatToolOutputPayload {\n messageId: string\n callId: string\n output: string\n stream: 'stdout' | 'stderr'\n}\n\nexport interface ChatTodoPayload {\n todos: Todo[]\n}\n\nexport interface ChatSummaryPayload {\n summary: string\n}\n\nexport interface ChatProgressPayload {\n message: string\n phase?: 'summary' | 'mode_switch' | 'starting' | 'context_warning' | 'context_error'\n}\n\nexport interface ChatFormatRetryPayload {\n attempt: number\n maxAttempts: number\n}\n\nexport interface ChatMessagePayload {\n message: Message\n}\n\nexport interface ChatMessageUpdatedPayload {\n messageId: string\n updates: Partial<Pick<Message, 'content' | 'thinkingContent' | 'toolCalls' | 'isStreaming' | 'stats' | 'promptContext' | 'partial'>>\n}\n\nexport interface ChatDonePayload {\n messageId: string\n reason: 'complete' | 'stopped' | 'error' | 'waiting_for_user'\n stats?: {\n model: string\n mode: ToolMode // Which system prompt was used (planner, builder, verifier)\n totalTime: number\n toolTime: number\n prefillTokens: number\n prefillSpeed: number\n generationTokens: number\n generationSpeed: number\n }\n}\n\nexport interface ChatErrorPayload {\n error: string\n recoverable: boolean\n}\n\n// Path confirmation payloads\nexport type PathConfirmationReason = 'outside_workdir' | 'sensitive_file' | 'both'\n\nexport interface ChatPathConfirmationPayload {\n callId: string\n tool: string\n paths: string[] // The paths requiring confirmation\n workdir: string // For context in UI\n reason: PathConfirmationReason // Why confirmation is needed\n}\n\n// Client payload for path confirmation response\nexport interface PathConfirmPayload {\n callId: string\n approved: boolean\n}\n\n// Mode payloads\nexport interface ModeChangedPayload {\n mode: SessionMode\n auto: boolean // Was this an automatic switch?\n reason?: string\n}\n\n// Phase payloads\nexport interface PhaseChangedPayload {\n phase: SessionPhase\n}\n\n// Task completion payloads\nexport interface TaskCompletedPayload {\n summary: string | null\n iterations: number\n totalTimeSeconds: number\n totalToolCalls: number\n totalTokensGenerated: number\n avgGenerationSpeed: number\n responseCount: number\n llmCallCount: number\n criteria: Array<{ id: string; description: string; status: string }>\n workflowName?: string\n workflowId?: string\n workflowColor?: string\n}\n\n// Criteria payloads\nexport interface CriteriaUpdatedPayload {\n criteria: Criterion[]\n changedId?: string // Which criterion changed, if specific\n}\n\n// Context payloads\nexport interface ContextStatePayload {\n context: ContextState\n}\n\n// Provider payloads (server → client)\nexport interface ProviderChangedPayload {\n providerId: string\n providerName: string\n model: string\n backend: string\n}\n\n// Queue payloads (server → client)\nexport interface QueueStatePayload {\n messages: QueuedMessage[]\n}\n\n// Other payloads\nexport interface LspDiagnosticsPayload {\n path: string\n diagnostics: Diagnostic[]\n}\n\nexport interface ErrorPayload {\n code: string\n message: string\n details?: unknown\n}\n\n// ============================================================================\n// Chat Events (unified streaming events)\n// ============================================================================\n\n// All chat events use the server message types above (chat.delta, chat.tool_call, etc.)\n// These are sent via ServerMessage with the corresponding payload types.\n\n// Special events that may trigger mode changes or UI updates:\nexport interface ContextCompactionEvent {\n beforeTokens: number\n afterTokens: number\n}\n\nexport interface AskUserEvent {\n question: string\n callId: string\n}\n\n// Agent events (used by runAgent in agent/runner.ts)\nexport type AgentEvent =\n | { type: 'aborted' }\n | { type: 'text_delta'; content: string }\n | { type: 'thinking'; content: string }\n | { type: 'error'; error: string; recoverable: boolean }\n | { type: 'context_compaction'; beforeTokens: number; afterTokens: number }\n | { type: 'done'; allCriteriaPassed: boolean; summary: string; stats: ChatDonePayload['stats'] }\n | { type: 'stuck'; reason: string; failedAttempts: number }\n | { type: 'tool_call'; callId: string; tool: string; args: Record<string, unknown> }\n | { type: 'tool_result'; callId: string; tool: string; result: ToolResult }\n | { type: 'tool_error'; callId: string; tool: string; error: string; willRetry: boolean }\n | { type: 'ask_user'; question: string; callId: string }\n | { type: 'format_retry'; attempt: number; maxAttempts: number }\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nexport function createClientMessage<T>(\n type: ClientMessageType,\n payload: T\n): ClientMessage<T> {\n return {\n id: crypto.randomUUID(),\n type,\n payload,\n }\n}\n\nexport function createServerMessage<T>(\n type: ServerMessageType,\n payload: T,\n correlationId?: string\n): ServerMessage<T> {\n const message: ServerMessage<T> = { type, payload }\n if (correlationId !== undefined) {\n message.id = correlationId\n }\n return message\n}\n\n// Type guards\nexport function isClientMessage(msg: unknown): msg is ClientMessage {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n 'id' in msg &&\n 'type' in msg &&\n 'payload' in msg\n )\n}\n\nexport function isServerMessage(msg: unknown): msg is ServerMessage {\n return (\n typeof msg === 'object' &&\n msg !== null &&\n 'type' in msg &&\n 'payload' in msg\n )\n}\n"],"mappings":";AAudO,SAAS,oBACd,MACA,SACkB;AAClB,SAAO;AAAA,IACL,IAAI,OAAO,WAAW;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,oBACd,MACA,SACA,eACkB;AAClB,QAAM,UAA4B,EAAE,MAAM,QAAQ;AAClD,MAAI,kBAAkB,QAAW;AAC/B,YAAQ,KAAK;AAAA,EACf;AACA,SAAO;AACT;AAGO,SAAS,gBAAgB,KAAoC;AAClE,SACE,OAAO,QAAQ,YACf,QAAQ,QACR,QAAQ,OACR,UAAU,OACV,aAAa;AAEjB;AAEO,SAAS,gBAAgB,KAAoC;AAClE,SACE,OAAO,QAAQ,YACf,QAAQ,QACR,UAAU,OACV,aAAa;AAEjB;","names":[]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDatabase
|
|
3
|
+
} from "./chunk-4Z2OZ6NX.js";
|
|
4
|
+
|
|
5
|
+
// src/server/db/settings.ts
|
|
6
|
+
var SETTINGS_KEYS = {
|
|
7
|
+
GLOBAL_INSTRUCTIONS: "global_instructions"
|
|
8
|
+
};
|
|
9
|
+
function getSetting(key) {
|
|
10
|
+
const db = getDatabase();
|
|
11
|
+
const row = db.prepare(`
|
|
12
|
+
SELECT value FROM settings WHERE key = ?
|
|
13
|
+
`).get(key);
|
|
14
|
+
return row?.value ?? null;
|
|
15
|
+
}
|
|
16
|
+
function setSetting(key, value) {
|
|
17
|
+
const db = getDatabase();
|
|
18
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19
|
+
db.prepare(`
|
|
20
|
+
INSERT INTO settings (key, value, updated_at)
|
|
21
|
+
VALUES (?, ?, ?)
|
|
22
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
|
|
23
|
+
`).run(key, value, now);
|
|
24
|
+
}
|
|
25
|
+
function deleteSetting(key) {
|
|
26
|
+
const db = getDatabase();
|
|
27
|
+
db.prepare(`DELETE FROM settings WHERE key = ?`).run(key);
|
|
28
|
+
}
|
|
29
|
+
function getAllSettings() {
|
|
30
|
+
const db = getDatabase();
|
|
31
|
+
const rows = db.prepare(`SELECT key, value FROM settings`).all();
|
|
32
|
+
const result = {};
|
|
33
|
+
for (const row of rows) {
|
|
34
|
+
result[row.key] = row.value;
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
SETTINGS_KEYS,
|
|
41
|
+
getSetting,
|
|
42
|
+
setSetting,
|
|
43
|
+
deleteSetting,
|
|
44
|
+
getAllSettings
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=chunk-MTX5Y3V3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/db/settings.ts"],"sourcesContent":["import { getDatabase } from './index.js'\n\n// ============================================================================\n// Settings Operations\n// ============================================================================\n\n// Well-known settings keys\nexport const SETTINGS_KEYS = {\n GLOBAL_INSTRUCTIONS: 'global_instructions',\n} as const\n\nexport type SettingsKey = typeof SETTINGS_KEYS[keyof typeof SETTINGS_KEYS]\n\ninterface SettingsRow {\n key: string\n value: string\n updated_at: string\n}\n\n/**\n * Get a setting value by key.\n */\nexport function getSetting(key: string): string | null {\n const db = getDatabase()\n \n const row = db.prepare(`\n SELECT value FROM settings WHERE key = ?\n `).get(key) as { value: string } | undefined\n \n return row?.value ?? null\n}\n\n/**\n * Set a setting value. Creates if not exists, updates if exists.\n */\nexport function setSetting(key: string, value: string): void {\n const db = getDatabase()\n const now = new Date().toISOString()\n \n db.prepare(`\n INSERT INTO settings (key, value, updated_at)\n VALUES (?, ?, ?)\n ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at\n `).run(key, value, now)\n}\n\n/**\n * Delete a setting by key.\n */\nexport function deleteSetting(key: string): void {\n const db = getDatabase()\n db.prepare(`DELETE FROM settings WHERE key = ?`).run(key)\n}\n\n/**\n * Get all settings as a key-value object.\n */\nexport function getAllSettings(): Record<string, string> {\n const db = getDatabase()\n \n const rows = db.prepare(`SELECT key, value FROM settings`).all() as SettingsRow[]\n \n const result: Record<string, string> = {}\n for (const row of rows) {\n result[row.key] = row.value\n }\n return result\n}\n"],"mappings":";;;;;AAOO,IAAM,gBAAgB;AAAA,EAC3B,qBAAqB;AACvB;AAaO,SAAS,WAAW,KAA4B;AACrD,QAAM,KAAK,YAAY;AAEvB,QAAM,MAAM,GAAG,QAAQ;AAAA;AAAA,GAEtB,EAAE,IAAI,GAAG;AAEV,SAAO,KAAK,SAAS;AACvB;AAKO,SAAS,WAAW,KAAa,OAAqB;AAC3D,QAAM,KAAK,YAAY;AACvB,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AAEnC,KAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,GAIV,EAAE,IAAI,KAAK,OAAO,GAAG;AACxB;AAKO,SAAS,cAAc,KAAmB;AAC/C,QAAM,KAAK,YAAY;AACvB,KAAG,QAAQ,oCAAoC,EAAE,IAAI,GAAG;AAC1D;AAKO,SAAS,iBAAyC;AACvD,QAAM,KAAK,YAAY;AAEvB,QAAM,OAAO,GAAG,QAAQ,iCAAiC,EAAE,IAAI;AAE/D,QAAM,SAAiC,CAAC;AACxC,aAAW,OAAO,MAAM;AACtB,WAAO,IAAI,GAAG,IAAI,IAAI;AAAA,EACxB;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDatabase
|
|
3
|
+
} from "./chunk-4Z2OZ6NX.js";
|
|
4
|
+
|
|
5
|
+
// src/server/db/projects.ts
|
|
6
|
+
function createProject(name, workdir) {
|
|
7
|
+
const db = getDatabase();
|
|
8
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9
|
+
const id = crypto.randomUUID();
|
|
10
|
+
db.prepare(`
|
|
11
|
+
INSERT INTO projects (id, name, workdir, created_at, updated_at)
|
|
12
|
+
VALUES (?, ?, ?, ?, ?)
|
|
13
|
+
`).run(id, name, workdir, now, now);
|
|
14
|
+
return {
|
|
15
|
+
id,
|
|
16
|
+
name,
|
|
17
|
+
workdir,
|
|
18
|
+
createdAt: now,
|
|
19
|
+
updatedAt: now
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function getProject(id) {
|
|
23
|
+
const db = getDatabase();
|
|
24
|
+
const row = db.prepare(`
|
|
25
|
+
SELECT * FROM projects WHERE id = ?
|
|
26
|
+
`).get(id);
|
|
27
|
+
if (!row) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return rowToProject(row);
|
|
31
|
+
}
|
|
32
|
+
function listProjects() {
|
|
33
|
+
const db = getDatabase();
|
|
34
|
+
const rows = db.prepare(`
|
|
35
|
+
SELECT * FROM projects ORDER BY updated_at DESC
|
|
36
|
+
`).all();
|
|
37
|
+
return rows.map(rowToProject);
|
|
38
|
+
}
|
|
39
|
+
function updateProject(id, updates) {
|
|
40
|
+
const db = getDatabase();
|
|
41
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
42
|
+
const sets = ["updated_at = ?"];
|
|
43
|
+
const values = [now];
|
|
44
|
+
if (updates.name !== void 0) {
|
|
45
|
+
sets.push("name = ?");
|
|
46
|
+
values.push(updates.name);
|
|
47
|
+
}
|
|
48
|
+
if (updates.customInstructions !== void 0) {
|
|
49
|
+
sets.push("custom_instructions = ?");
|
|
50
|
+
values.push(updates.customInstructions);
|
|
51
|
+
}
|
|
52
|
+
values.push(id);
|
|
53
|
+
const result = db.prepare(`
|
|
54
|
+
UPDATE projects SET ${sets.join(", ")} WHERE id = ?
|
|
55
|
+
`).run(...values);
|
|
56
|
+
if (result.changes === 0) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return getProject(id);
|
|
60
|
+
}
|
|
61
|
+
function deleteProject(id) {
|
|
62
|
+
const db = getDatabase();
|
|
63
|
+
db.prepare("DELETE FROM projects WHERE id = ?").run(id);
|
|
64
|
+
}
|
|
65
|
+
function rowToProject(row) {
|
|
66
|
+
return {
|
|
67
|
+
id: row.id,
|
|
68
|
+
name: row.name,
|
|
69
|
+
workdir: row.workdir,
|
|
70
|
+
...row.custom_instructions ? { customInstructions: row.custom_instructions } : {},
|
|
71
|
+
createdAt: row.created_at,
|
|
72
|
+
updatedAt: row.updated_at
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export {
|
|
77
|
+
createProject,
|
|
78
|
+
getProject,
|
|
79
|
+
listProjects,
|
|
80
|
+
updateProject,
|
|
81
|
+
deleteProject
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=chunk-N2XYX4ZA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/db/projects.ts"],"sourcesContent":["import type { Project } from '../../shared/types.js'\nimport { getDatabase } from './index.js'\n\n// ============================================================================\n// Project Operations\n// ============================================================================\n\nexport function createProject(name: string, workdir: string): Project {\n const db = getDatabase()\n const now = new Date().toISOString()\n const id = crypto.randomUUID()\n \n db.prepare(`\n INSERT INTO projects (id, name, workdir, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?)\n `).run(id, name, workdir, now, now)\n \n return {\n id,\n name,\n workdir,\n createdAt: now,\n updatedAt: now,\n }\n}\n\nexport function getProject(id: string): Project | null {\n const db = getDatabase()\n \n const row = db.prepare(`\n SELECT * FROM projects WHERE id = ?\n `).get(id) as ProjectRow | undefined\n \n if (!row) {\n return null\n }\n \n return rowToProject(row)\n}\n\nexport function getProjectByWorkdir(workdir: string): Project | null {\n const db = getDatabase()\n \n const row = db.prepare(`\n SELECT * FROM projects WHERE workdir = ?\n `).get(workdir) as ProjectRow | undefined\n \n if (!row) {\n return null\n }\n \n return rowToProject(row)\n}\n\nexport function listProjects(): Project[] {\n const db = getDatabase()\n \n const rows = db.prepare(`\n SELECT * FROM projects ORDER BY updated_at DESC\n `).all() as ProjectRow[]\n \n return rows.map(rowToProject)\n}\n\nexport function updateProject(id: string, updates: { name?: string; customInstructions?: string | null }): Project | null {\n const db = getDatabase()\n const now = new Date().toISOString()\n \n const sets: string[] = ['updated_at = ?']\n const values: (string | null)[] = [now]\n \n if (updates.name !== undefined) {\n sets.push('name = ?')\n values.push(updates.name)\n }\n \n if (updates.customInstructions !== undefined) {\n sets.push('custom_instructions = ?')\n values.push(updates.customInstructions)\n }\n \n values.push(id)\n \n const result = db.prepare(`\n UPDATE projects SET ${sets.join(', ')} WHERE id = ?\n `).run(...values)\n \n // Check if any row was updated\n if (result.changes === 0) {\n return null\n }\n \n return getProject(id)\n}\n\nexport function deleteProject(id: string): void {\n const db = getDatabase()\n // Sessions will be cascade deleted due to foreign key\n db.prepare('DELETE FROM projects WHERE id = ?').run(id)\n}\n\n// ============================================================================\n// Row Types\n// ============================================================================\n\ninterface ProjectRow {\n id: string\n name: string\n workdir: string\n custom_instructions: string | null\n created_at: string\n updated_at: string\n}\n\nfunction rowToProject(row: ProjectRow): Project {\n return {\n id: row.id,\n name: row.name,\n workdir: row.workdir,\n ...(row.custom_instructions ? { customInstructions: row.custom_instructions } : {}),\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n"],"mappings":";;;;;AAOO,SAAS,cAAc,MAAc,SAA0B;AACpE,QAAM,KAAK,YAAY;AACvB,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,QAAM,KAAK,OAAO,WAAW;AAE7B,KAAG,QAAQ;AAAA;AAAA;AAAA,GAGV,EAAE,IAAI,IAAI,MAAM,SAAS,KAAK,GAAG;AAElC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACF;AAEO,SAAS,WAAW,IAA4B;AACrD,QAAM,KAAK,YAAY;AAEvB,QAAM,MAAM,GAAG,QAAQ;AAAA;AAAA,GAEtB,EAAE,IAAI,EAAE;AAET,MAAI,CAAC,KAAK;AACR,WAAO;AAAA,EACT;AAEA,SAAO,aAAa,GAAG;AACzB;AAgBO,SAAS,eAA0B;AACxC,QAAM,KAAK,YAAY;AAEvB,QAAM,OAAO,GAAG,QAAQ;AAAA;AAAA,GAEvB,EAAE,IAAI;AAEP,SAAO,KAAK,IAAI,YAAY;AAC9B;AAEO,SAAS,cAAc,IAAY,SAAgF;AACxH,QAAM,KAAK,YAAY;AACvB,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AAEnC,QAAM,OAAiB,CAAC,gBAAgB;AACxC,QAAM,SAA4B,CAAC,GAAG;AAEtC,MAAI,QAAQ,SAAS,QAAW;AAC9B,SAAK,KAAK,UAAU;AACpB,WAAO,KAAK,QAAQ,IAAI;AAAA,EAC1B;AAEA,MAAI,QAAQ,uBAAuB,QAAW;AAC5C,SAAK,KAAK,yBAAyB;AACnC,WAAO,KAAK,QAAQ,kBAAkB;AAAA,EACxC;AAEA,SAAO,KAAK,EAAE;AAEd,QAAM,SAAS,GAAG,QAAQ;AAAA,0BACF,KAAK,KAAK,IAAI,CAAC;AAAA,GACtC,EAAE,IAAI,GAAG,MAAM;AAGhB,MAAI,OAAO,YAAY,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,SAAO,WAAW,EAAE;AACtB;AAEO,SAAS,cAAc,IAAkB;AAC9C,QAAM,KAAK,YAAY;AAEvB,KAAG,QAAQ,mCAAmC,EAAE,IAAI,EAAE;AACxD;AAeA,SAAS,aAAa,KAA0B;AAC9C,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,MAAM,IAAI;AAAA,IACV,SAAS,IAAI;AAAA,IACb,GAAI,IAAI,sBAAsB,EAAE,oBAAoB,IAAI,oBAAoB,IAAI,CAAC;AAAA,IACjF,WAAW,IAAI;AAAA,IACf,WAAW,IAAI;AAAA,EACjB;AACF;","names":[]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// src/shared/stats.ts
|
|
2
|
+
var roundTo1 = (n) => Math.round(n * 10) / 10;
|
|
3
|
+
function getStatsIdentity(stats) {
|
|
4
|
+
return {
|
|
5
|
+
providerId: stats.providerId,
|
|
6
|
+
providerName: stats.providerName,
|
|
7
|
+
backend: stats.backend,
|
|
8
|
+
model: stats.model
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function getModelGroupKey(identity) {
|
|
12
|
+
return `${identity.providerId}::${identity.model}`;
|
|
13
|
+
}
|
|
14
|
+
function getModelGroupLabel(identity) {
|
|
15
|
+
return `${identity.providerName} > ${identity.model}`;
|
|
16
|
+
}
|
|
17
|
+
function buildSessionStats(messagesWithStats) {
|
|
18
|
+
let totalTime = 0;
|
|
19
|
+
let toolTime = 0;
|
|
20
|
+
let prefillTokens = 0;
|
|
21
|
+
let generationTokens = 0;
|
|
22
|
+
let totalPrefillTime = 0;
|
|
23
|
+
let totalGenTime = 0;
|
|
24
|
+
const dataPoints = [];
|
|
25
|
+
const callDataPoints = [];
|
|
26
|
+
let sessionCallIndex = 0;
|
|
27
|
+
for (const [index, msg] of messagesWithStats.entries()) {
|
|
28
|
+
const stats = msg.stats;
|
|
29
|
+
const identity = getStatsIdentity(stats);
|
|
30
|
+
totalTime += stats.totalTime;
|
|
31
|
+
toolTime += stats.toolTime;
|
|
32
|
+
prefillTokens += stats.prefillTokens;
|
|
33
|
+
generationTokens += stats.generationTokens;
|
|
34
|
+
const prefillTime = stats.prefillSpeed > 0 ? stats.prefillTokens / stats.prefillSpeed : 0;
|
|
35
|
+
const genTime = stats.generationSpeed > 0 ? stats.generationTokens / stats.generationSpeed : 0;
|
|
36
|
+
totalPrefillTime += prefillTime;
|
|
37
|
+
totalGenTime += genTime;
|
|
38
|
+
dataPoints.push({
|
|
39
|
+
messageId: msg.id,
|
|
40
|
+
timestamp: msg.timestamp,
|
|
41
|
+
...identity,
|
|
42
|
+
mode: stats.mode,
|
|
43
|
+
responseIndex: index + 1,
|
|
44
|
+
prefillTokens: stats.prefillTokens,
|
|
45
|
+
generationTokens: stats.generationTokens,
|
|
46
|
+
prefillSpeed: stats.prefillSpeed,
|
|
47
|
+
generationSpeed: stats.generationSpeed,
|
|
48
|
+
totalTime: stats.totalTime,
|
|
49
|
+
aiTime: stats.totalTime - stats.toolTime,
|
|
50
|
+
toolTime: stats.toolTime
|
|
51
|
+
});
|
|
52
|
+
const llmCalls = stats.llmCalls ?? [];
|
|
53
|
+
for (const call of llmCalls) {
|
|
54
|
+
sessionCallIndex += 1;
|
|
55
|
+
callDataPoints.push({
|
|
56
|
+
messageId: msg.id,
|
|
57
|
+
timestamp: call.timestamp ?? msg.timestamp,
|
|
58
|
+
providerId: call.providerId,
|
|
59
|
+
providerName: call.providerName,
|
|
60
|
+
backend: call.backend,
|
|
61
|
+
model: call.model,
|
|
62
|
+
mode: stats.mode,
|
|
63
|
+
responseIndex: index + 1,
|
|
64
|
+
sessionCallIndex,
|
|
65
|
+
callIndex: call.callIndex,
|
|
66
|
+
promptTokens: call.promptTokens,
|
|
67
|
+
completionTokens: call.completionTokens,
|
|
68
|
+
ttft: call.ttft,
|
|
69
|
+
completionTime: call.completionTime,
|
|
70
|
+
prefillSpeed: call.prefillSpeed,
|
|
71
|
+
generationSpeed: call.generationSpeed,
|
|
72
|
+
totalTime: call.totalTime
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const avgPrefillSpeed = totalPrefillTime > 0 ? roundTo1(prefillTokens / totalPrefillTime) : 0;
|
|
77
|
+
const avgGenerationSpeed = totalGenTime > 0 ? roundTo1(generationTokens / totalGenTime) : 0;
|
|
78
|
+
return {
|
|
79
|
+
totalTime: roundTo1(totalTime),
|
|
80
|
+
aiTime: roundTo1(totalTime - toolTime),
|
|
81
|
+
toolTime: roundTo1(toolTime),
|
|
82
|
+
prefillTokens,
|
|
83
|
+
generationTokens,
|
|
84
|
+
avgPrefillSpeed,
|
|
85
|
+
avgGenerationSpeed,
|
|
86
|
+
responseCount: messagesWithStats.length,
|
|
87
|
+
llmCallCount: callDataPoints.length,
|
|
88
|
+
dataPoints,
|
|
89
|
+
callDataPoints
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function computeSessionStats(messages) {
|
|
93
|
+
const messagesWithStats = messages.filter(
|
|
94
|
+
(msg) => msg.stats !== void 0 && msg.stats !== null
|
|
95
|
+
).sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
96
|
+
if (messagesWithStats.length === 0) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const modelBuckets = /* @__PURE__ */ new Map();
|
|
100
|
+
for (const message of messagesWithStats) {
|
|
101
|
+
const key = getModelGroupKey(getStatsIdentity(message.stats));
|
|
102
|
+
const existing = modelBuckets.get(key) ?? [];
|
|
103
|
+
modelBuckets.set(key, [...existing, message]);
|
|
104
|
+
}
|
|
105
|
+
const modelGroups = Array.from(modelBuckets.entries()).map(([key, groupMessages]) => {
|
|
106
|
+
const identity = getStatsIdentity(groupMessages[0].stats);
|
|
107
|
+
const groupStats = buildSessionStats(groupMessages);
|
|
108
|
+
return {
|
|
109
|
+
...identity,
|
|
110
|
+
key,
|
|
111
|
+
label: getModelGroupLabel(identity),
|
|
112
|
+
...groupStats
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
...buildSessionStats(messagesWithStats),
|
|
117
|
+
modelGroups
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export {
|
|
122
|
+
computeSessionStats
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=chunk-QY7BMXWT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/stats.ts"],"sourcesContent":["/**\n * Session stats computation - aggregates response-level MessageStats from\n * multiple assistant messages into SessionStats for benchmarking and trends.\n */\n\nimport type { CallStatsDataPoint, Message, MessageStats, ModelSessionStats, SessionStats, StatsDataPoint, StatsIdentity } from './types.js'\n\nconst roundTo1 = (n: number): number => Math.round(n * 10) / 10\n\ntype MessageWithStats = Message & { stats: NonNullable<Message['stats']> }\n\nfunction getStatsIdentity(stats: MessageStats): StatsIdentity {\n return {\n providerId: stats.providerId,\n providerName: stats.providerName,\n backend: stats.backend,\n model: stats.model,\n }\n}\n\nfunction getModelGroupKey(identity: StatsIdentity): string {\n return `${identity.providerId}::${identity.model}`\n}\n\nfunction getModelGroupLabel(identity: StatsIdentity): string {\n return `${identity.providerName} > ${identity.model}`\n}\n\nfunction buildSessionStats(messagesWithStats: MessageWithStats[]): Omit<SessionStats, 'modelGroups'> {\n let totalTime = 0\n let toolTime = 0\n let prefillTokens = 0\n let generationTokens = 0\n let totalPrefillTime = 0\n let totalGenTime = 0\n\n const dataPoints: StatsDataPoint[] = []\n const callDataPoints: CallStatsDataPoint[] = []\n let sessionCallIndex = 0\n\n for (const [index, msg] of messagesWithStats.entries()) {\n const stats = msg.stats\n const identity = getStatsIdentity(stats)\n\n totalTime += stats.totalTime\n toolTime += stats.toolTime\n prefillTokens += stats.prefillTokens\n generationTokens += stats.generationTokens\n\n const prefillTime = stats.prefillSpeed > 0 ? stats.prefillTokens / stats.prefillSpeed : 0\n const genTime = stats.generationSpeed > 0 ? stats.generationTokens / stats.generationSpeed : 0\n\n totalPrefillTime += prefillTime\n totalGenTime += genTime\n\n dataPoints.push({\n messageId: msg.id,\n timestamp: msg.timestamp,\n ...identity,\n mode: stats.mode,\n responseIndex: index + 1,\n prefillTokens: stats.prefillTokens,\n generationTokens: stats.generationTokens,\n prefillSpeed: stats.prefillSpeed,\n generationSpeed: stats.generationSpeed,\n totalTime: stats.totalTime,\n aiTime: stats.totalTime - stats.toolTime,\n toolTime: stats.toolTime,\n })\n\n const llmCalls = stats.llmCalls ?? []\n for (const call of llmCalls) {\n sessionCallIndex += 1\n callDataPoints.push({\n messageId: msg.id,\n timestamp: call.timestamp ?? msg.timestamp,\n providerId: call.providerId,\n providerName: call.providerName,\n backend: call.backend,\n model: call.model,\n mode: stats.mode,\n responseIndex: index + 1,\n sessionCallIndex,\n callIndex: call.callIndex,\n promptTokens: call.promptTokens,\n completionTokens: call.completionTokens,\n ttft: call.ttft,\n completionTime: call.completionTime,\n prefillSpeed: call.prefillSpeed,\n generationSpeed: call.generationSpeed,\n totalTime: call.totalTime,\n })\n }\n }\n\n const avgPrefillSpeed = totalPrefillTime > 0 ? roundTo1(prefillTokens / totalPrefillTime) : 0\n const avgGenerationSpeed = totalGenTime > 0 ? roundTo1(generationTokens / totalGenTime) : 0\n\n return {\n totalTime: roundTo1(totalTime),\n aiTime: roundTo1(totalTime - toolTime),\n toolTime: roundTo1(toolTime),\n prefillTokens,\n generationTokens,\n avgPrefillSpeed,\n avgGenerationSpeed,\n responseCount: messagesWithStats.length,\n llmCallCount: callDataPoints.length,\n dataPoints,\n callDataPoints,\n }\n}\n\n/**\n * Compute aggregated session stats from an array of messages.\n * \n * Returns null if no messages have stats.\n * \n * Weighted average for speeds: totalTokens / totalTime\n * This gives accurate average throughput across varying context sizes.\n */\nexport function computeSessionStats(messages: Message[]): SessionStats | null {\n const messagesWithStats = messages.filter(\n (msg): msg is MessageWithStats => msg.stats !== undefined && msg.stats !== null\n ).sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime())\n\n if (messagesWithStats.length === 0) {\n return null\n }\n\n const modelBuckets = new Map<string, MessageWithStats[]>()\n for (const message of messagesWithStats) {\n const key = getModelGroupKey(getStatsIdentity(message.stats))\n const existing = modelBuckets.get(key) ?? []\n modelBuckets.set(key, [...existing, message])\n }\n\n const modelGroups: ModelSessionStats[] = Array.from(modelBuckets.entries()).map(([key, groupMessages]) => {\n const identity = getStatsIdentity(groupMessages[0]!.stats)\n const groupStats = buildSessionStats(groupMessages)\n return {\n ...identity,\n key,\n label: getModelGroupLabel(identity),\n ...groupStats,\n }\n })\n\n return {\n ...buildSessionStats(messagesWithStats),\n modelGroups,\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/server/utils/logger.ts
|
|
2
|
+
var LEVELS = {
|
|
3
|
+
debug: 0,
|
|
4
|
+
info: 1,
|
|
5
|
+
warn: 2,
|
|
6
|
+
error: 3
|
|
7
|
+
};
|
|
8
|
+
var currentLevel = "info";
|
|
9
|
+
function setLogLevel(level, mode) {
|
|
10
|
+
if (mode && !level) {
|
|
11
|
+
currentLevel = mode === "development" ? "debug" : "warn";
|
|
12
|
+
} else {
|
|
13
|
+
currentLevel = level ?? "info";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function shouldLog(level) {
|
|
17
|
+
return LEVELS[level] >= LEVELS[currentLevel];
|
|
18
|
+
}
|
|
19
|
+
function formatMessage(level, message, context) {
|
|
20
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
21
|
+
const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
|
|
22
|
+
if (context && Object.keys(context).length > 0) {
|
|
23
|
+
return `${prefix} ${message} ${JSON.stringify(context)}`;
|
|
24
|
+
}
|
|
25
|
+
return `${prefix} ${message}`;
|
|
26
|
+
}
|
|
27
|
+
var logger = {
|
|
28
|
+
debug(message, context) {
|
|
29
|
+
if (shouldLog("debug")) {
|
|
30
|
+
console.debug(formatMessage("debug", message, context));
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
info(message, context) {
|
|
34
|
+
if (shouldLog("info")) {
|
|
35
|
+
console.info(formatMessage("info", message, context));
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
warn(message, context) {
|
|
39
|
+
if (shouldLog("warn")) {
|
|
40
|
+
console.warn(formatMessage("warn", message, context));
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
error(message, context) {
|
|
44
|
+
if (shouldLog("error")) {
|
|
45
|
+
console.error(formatMessage("error", message, context));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
setLogLevel,
|
|
52
|
+
logger
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=chunk-U62ZECSQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/utils/logger.ts"],"sourcesContent":["type LogLevel = 'debug' | 'info' | 'warn' | 'error'\ntype Mode = 'development' | 'production'\n\nconst LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n}\n\nlet currentLevel: LogLevel = 'info'\n\nexport function setLogLevel(level?: LogLevel, mode?: Mode): void {\n // Use mode-based default if no level specified\n if (mode && !level) {\n currentLevel = mode === 'development' ? 'debug' : 'warn'\n } else {\n currentLevel = level ?? 'info'\n }\n}\n\nexport function getDefaultLogLevel(mode: Mode): LogLevel {\n return mode === 'development' ? 'debug' : 'warn'\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return LEVELS[level] >= LEVELS[currentLevel]\n}\n\nfunction formatMessage(level: LogLevel, message: string, context?: Record<string, unknown>): string {\n const timestamp = new Date().toISOString()\n const prefix = `[${timestamp}] [${level.toUpperCase()}]`\n \n if (context && Object.keys(context).length > 0) {\n return `${prefix} ${message} ${JSON.stringify(context)}`\n }\n \n return `${prefix} ${message}`\n}\n\nexport const logger = {\n debug(message: string, context?: Record<string, unknown>): void {\n if (shouldLog('debug')) {\n console.debug(formatMessage('debug', message, context))\n }\n },\n \n info(message: string, context?: Record<string, unknown>): void {\n if (shouldLog('info')) {\n console.info(formatMessage('info', message, context))\n }\n },\n \n warn(message: string, context?: Record<string, unknown>): void {\n if (shouldLog('warn')) {\n console.warn(formatMessage('warn', message, context))\n }\n },\n \n error(message: string, context?: Record<string, unknown>): void {\n if (shouldLog('error')) {\n console.error(formatMessage('error', message, context))\n }\n },\n}\n"],"mappings":";AAGA,IAAM,SAAmC;AAAA,EACvC,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,IAAI,eAAyB;AAEtB,SAAS,YAAY,OAAkB,MAAmB;AAE/D,MAAI,QAAQ,CAAC,OAAO;AAClB,mBAAe,SAAS,gBAAgB,UAAU;AAAA,EACpD,OAAO;AACL,mBAAe,SAAS;AAAA,EAC1B;AACF;AAMA,SAAS,UAAU,OAA0B;AAC3C,SAAO,OAAO,KAAK,KAAK,OAAO,YAAY;AAC7C;AAEA,SAAS,cAAc,OAAiB,SAAiB,SAA2C;AAClG,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,SAAS,IAAI,SAAS,MAAM,MAAM,YAAY,CAAC;AAErD,MAAI,WAAW,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG;AAC9C,WAAO,GAAG,MAAM,IAAI,OAAO,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,EACxD;AAEA,SAAO,GAAG,MAAM,IAAI,OAAO;AAC7B;AAEO,IAAM,SAAS;AAAA,EACpB,MAAM,SAAiB,SAAyC;AAC9D,QAAI,UAAU,OAAO,GAAG;AACtB,cAAQ,MAAM,cAAc,SAAS,SAAS,OAAO,CAAC;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,KAAK,SAAiB,SAAyC;AAC7D,QAAI,UAAU,MAAM,GAAG;AACrB,cAAQ,KAAK,cAAc,QAAQ,SAAS,OAAO,CAAC;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,KAAK,SAAiB,SAAyC;AAC7D,QAAI,UAAU,MAAM,GAAG;AACrB,cAAQ,KAAK,cAAc,QAAQ,SAAS,OAAO,CAAC;AAAA,IACtD;AAAA,EACF;AAAA,EAEA,MAAM,SAAiB,SAAyC;AAC9D,QAAI,UAAU,OAAO,GAAG;AACtB,cAAQ,MAAM,cAAc,SAAS,SAAS,OAAO,CAAC;AAAA,IACxD;AAAA,EACF;AACF;","names":[]}
|