kimi-proxy 0.1.3 → 0.1.5
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 +18 -0
- package/dist/config.d.ts +1 -0
- package/dist/core/clientAdapters.d.ts +1 -1
- package/dist/index.js +265 -110
- package/dist/index.js.map +11 -11
- package/dist/livestore/runtime.d.ts +1 -0
- package/frontend/dist/assets/JSONViewer-8576db4f.js +3 -0
- package/frontend/dist/assets/{index-fd87815b.js → index-2a0cb805.js} +78 -67
- package/frontend/dist/assets/{index-413f3c24.css → index-bdff0377.css} +1 -1
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/JSONViewer-516863bb.js +0 -3
package/README.md
CHANGED
|
@@ -137,6 +137,15 @@ The API runs on `http://127.0.0.1:8000` and serves the dashboard (built assets)
|
|
|
137
137
|
|
|
138
138
|
## Configuration
|
|
139
139
|
|
|
140
|
+
### Dashboard & LiveStore
|
|
141
|
+
|
|
142
|
+
Control LiveStore sync behavior via environment variables:
|
|
143
|
+
|
|
144
|
+
| Variable | Default | Description |
|
|
145
|
+
| ----------------------- | ------- | ----------------------------------------------------------------------------------------------- |
|
|
146
|
+
| `LIVESTORE_BATCH` | 50 | Batch size for dashboard sync (range: 1-500) |
|
|
147
|
+
| `LIVESTORE_MAX_RECORDS` | 500 | Memory sliding window - max records to keep in LiveStore. Set to 0 to disable (not recommended) |
|
|
148
|
+
|
|
140
149
|
### Providers
|
|
141
150
|
|
|
142
151
|
Set environment variables in `.env`:
|
|
@@ -169,6 +178,15 @@ models:
|
|
|
169
178
|
|
|
170
179
|
The web dashboard shows request/response logs and metrics. Access it at the root path when running the proxy. LiveStore metadata sync pulls from `/api/livestore/pull` in batches (size controlled by `LIVESTORE_BATCH`) and lazily fetches blobs on expansion. Build the dashboard with `bun run build:all` to serve static assets from the backend.
|
|
171
180
|
|
|
181
|
+
### Performance Features
|
|
182
|
+
|
|
183
|
+
- **Reverse-chronological loading**: Data loads from newest to oldest, providing immediate access to recent logs
|
|
184
|
+
- **Memory-efficient virtualization**: Uses TanStack Virtual to render only visible rows
|
|
185
|
+
- **Configurable sliding window**: Limit browser memory usage by setting `LIVESTORE_MAX_RECORDS` (see `.env.example`)
|
|
186
|
+
- **Automatic garbage collection**: Old records beyond the window limit are automatically purged
|
|
187
|
+
|
|
188
|
+
The dashboard uses reactive queries with TanStack Table and TanStack Virtual for fast, efficient rendering of large datasets.
|
|
189
|
+
|
|
172
190
|
## Development
|
|
173
191
|
|
|
174
192
|
```bash
|
package/dist/config.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare class OpenAIChatClientAdapter implements ClientAdapter {
|
|
|
9
9
|
export declare class AnthropicMessagesClientAdapter implements ClientAdapter {
|
|
10
10
|
clientFormat: ClientFormat;
|
|
11
11
|
toUlx(body: ClientRequest, headers: Record<string, string>): Request;
|
|
12
|
-
fromUlx(ulxResponse: Response): JsonValue;
|
|
12
|
+
fromUlx(ulxResponse: Response, _ulxRequest: Request): JsonValue;
|
|
13
13
|
}
|
|
14
14
|
export declare class OpenAIResponsesClientAdapter implements ClientAdapter {
|
|
15
15
|
clientFormat: ClientFormat;
|
package/dist/index.js
CHANGED
|
@@ -91,6 +91,7 @@ function loadConfig() {
|
|
|
91
91
|
const streamDelay = Number(process.env.STREAM_DELAY ?? "10");
|
|
92
92
|
const streamChunkSize = Number(process.env.STREAM_CHUNK_SIZE ?? "5");
|
|
93
93
|
const livestoreBatch = Number(process.env.LIVESTORE_BATCH ?? "50");
|
|
94
|
+
const livestoreMaxRecords = process.env.LIVESTORE_MAX_RECORDS ? Number(process.env.LIVESTORE_MAX_RECORDS) : 500;
|
|
94
95
|
const openai = resolveOpenAI();
|
|
95
96
|
const anthropic = resolveAnthropic();
|
|
96
97
|
const openrouter = resolveOpenRouter();
|
|
@@ -116,7 +117,10 @@ function loadConfig() {
|
|
|
116
117
|
server: { host, port },
|
|
117
118
|
logging: { dbPath, blobRoot },
|
|
118
119
|
streaming: { delay: streamDelay, chunkSize: streamChunkSize },
|
|
119
|
-
livestore: {
|
|
120
|
+
livestore: {
|
|
121
|
+
batchSize: Math.max(1, Math.min(500, livestoreBatch)),
|
|
122
|
+
maxRecords: livestoreMaxRecords
|
|
123
|
+
},
|
|
120
124
|
providers: { openai, anthropic, openrouter, vertex },
|
|
121
125
|
models: modelRegistry
|
|
122
126
|
};
|
|
@@ -768,12 +772,12 @@ class HybridLogStore {
|
|
|
768
772
|
const clauses = [];
|
|
769
773
|
const params = { limit };
|
|
770
774
|
if (checkpoint.timestamp) {
|
|
771
|
-
clauses.push(`(timestamp
|
|
775
|
+
clauses.push(`(timestamp < @ts OR (timestamp = @ts AND id < @id))`);
|
|
772
776
|
params.ts = checkpoint.timestamp;
|
|
773
|
-
params.id = checkpoint.id ??
|
|
777
|
+
params.id = checkpoint.id ?? Number.MAX_SAFE_INTEGER;
|
|
774
778
|
}
|
|
775
779
|
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
776
|
-
const rows = this.db.prepare(`SELECT * FROM logs ${where} ORDER BY datetime(timestamp), id LIMIT @limit`).all(params);
|
|
780
|
+
const rows = this.db.prepare(`SELECT * FROM logs ${where} ORDER BY datetime(timestamp) DESC, id DESC LIMIT @limit`).all(params);
|
|
777
781
|
return { items: rows, total: rows.length, page: 1, pageSize: rows.length };
|
|
778
782
|
}
|
|
779
783
|
resolveBlobPath(record, kind) {
|
|
@@ -1554,33 +1558,36 @@ class OpenAIChatClientAdapter {
|
|
|
1554
1558
|
};
|
|
1555
1559
|
}
|
|
1556
1560
|
fromUlx(ulxResponse, _ulxRequest) {
|
|
1557
|
-
const
|
|
1558
|
-
|
|
1559
|
-
let
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1561
|
+
const textParts = [];
|
|
1562
|
+
const reasoningParts = [];
|
|
1563
|
+
let toolCalls = undefined;
|
|
1564
|
+
for (const block of ulxResponse.output) {
|
|
1565
|
+
if (block.type === "message") {
|
|
1566
|
+
for (const entry of block.content) {
|
|
1567
|
+
if (entry.type === "reasoning") {
|
|
1568
|
+
reasoningParts.push(entry.text ?? "");
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
const meaningfulContent = block.content.filter((c) => c.type !== "reasoning");
|
|
1572
|
+
if (meaningfulContent.length > 0) {
|
|
1573
|
+
const mixed = contentToText(meaningfulContent);
|
|
1574
|
+
textParts.push(typeof mixed === "string" ? mixed : JSON.stringify(mixed));
|
|
1575
|
+
}
|
|
1576
|
+
if (block.tool_calls) {
|
|
1577
|
+
toolCalls = [...toolCalls ?? [], ...block.tool_calls];
|
|
1578
|
+
}
|
|
1579
|
+
} else if (block.type === "reasoning") {
|
|
1580
|
+
for (const entry of block.content) {
|
|
1581
|
+
if (entry.type === "reasoning") {
|
|
1582
|
+
reasoningParts.push(entry.text ?? "");
|
|
1583
|
+
}
|
|
1568
1584
|
}
|
|
1569
|
-
}
|
|
1570
|
-
if (textParts.length > 0) {
|
|
1571
|
-
content = textParts.join("");
|
|
1572
|
-
} else if (contentBlocks.content.some((c) => c.type === "image_url" || c.type === "json")) {
|
|
1573
|
-
const mixed = contentToText(contentBlocks.content.filter((c) => c.type !== "reasoning"));
|
|
1574
|
-
content = typeof mixed === "string" ? mixed : JSON.stringify(mixed);
|
|
1575
|
-
} else {
|
|
1576
|
-
content = null;
|
|
1577
|
-
}
|
|
1578
|
-
if (reasoningParts.length > 0) {
|
|
1579
|
-
reasoning_content = reasoningParts.join(`
|
|
1580
|
-
|
|
1581
|
-
`);
|
|
1582
1585
|
}
|
|
1583
1586
|
}
|
|
1587
|
+
const content = textParts.join("") || null;
|
|
1588
|
+
const reasoning_content = reasoningParts.join(`
|
|
1589
|
+
|
|
1590
|
+
`) || undefined;
|
|
1584
1591
|
return {
|
|
1585
1592
|
id: ulxResponse.id,
|
|
1586
1593
|
object: "chat.completion",
|
|
@@ -1591,9 +1598,9 @@ class OpenAIChatClientAdapter {
|
|
|
1591
1598
|
index: 0,
|
|
1592
1599
|
message: {
|
|
1593
1600
|
role: "assistant",
|
|
1594
|
-
content,
|
|
1595
1601
|
...reasoning_content ? { reasoning_content } : {},
|
|
1596
|
-
|
|
1602
|
+
content,
|
|
1603
|
+
tool_calls: toolCalls ? toolCalls.map((tc) => ({
|
|
1597
1604
|
id: tc.id,
|
|
1598
1605
|
type: "function",
|
|
1599
1606
|
function: {
|
|
@@ -1683,7 +1690,11 @@ class AnthropicMessagesClientAdapter {
|
|
|
1683
1690
|
function anthropicBlockToUlxContent(block) {
|
|
1684
1691
|
const declaredType = typeof block.type === "string" ? block.type : "";
|
|
1685
1692
|
if (declaredType === "thinking" || declaredType === "redacted_thinking" || typeof block.thinking === "string") {
|
|
1686
|
-
return
|
|
1693
|
+
return {
|
|
1694
|
+
type: "reasoning",
|
|
1695
|
+
text: block.thinking ?? block.text ?? "",
|
|
1696
|
+
data: block.signature ? { signature: block.signature } : declaredType === "redacted_thinking" ? { redacted: true } : undefined
|
|
1697
|
+
};
|
|
1687
1698
|
}
|
|
1688
1699
|
if (typeof block.text === "string") {
|
|
1689
1700
|
return { type: "text", text: block.text };
|
|
@@ -1799,37 +1810,57 @@ class AnthropicMessagesClientAdapter {
|
|
|
1799
1810
|
metadata: { clientFormat: this.clientFormat, headers }
|
|
1800
1811
|
};
|
|
1801
1812
|
}
|
|
1802
|
-
fromUlx(ulxResponse) {
|
|
1803
|
-
const
|
|
1804
|
-
const
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
if (
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1813
|
+
fromUlx(ulxResponse, _ulxRequest) {
|
|
1814
|
+
const thinkingBlocks = [];
|
|
1815
|
+
const textBlocks = [];
|
|
1816
|
+
const toolUseBlocks = [];
|
|
1817
|
+
for (const block of ulxResponse.output) {
|
|
1818
|
+
if (block.type === "reasoning") {
|
|
1819
|
+
for (const entry of block.content) {
|
|
1820
|
+
if (entry.type === "reasoning") {
|
|
1821
|
+
thinkingBlocks.push({
|
|
1822
|
+
type: "thinking",
|
|
1823
|
+
thinking: entry.text ?? "",
|
|
1824
|
+
signature: entry.data?.signature
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
} else if (block.type === "message") {
|
|
1829
|
+
const messageContent = contentToText(block.content);
|
|
1830
|
+
if (typeof messageContent === "string") {
|
|
1831
|
+
if (messageContent)
|
|
1832
|
+
textBlocks.push({ type: "text", text: messageContent });
|
|
1833
|
+
} else if (Array.isArray(messageContent)) {
|
|
1834
|
+
for (const item of messageContent) {
|
|
1835
|
+
if (item && typeof item === "object") {
|
|
1836
|
+
const b = item;
|
|
1837
|
+
if (b.type === "thinking") {
|
|
1838
|
+
thinkingBlocks.push(b);
|
|
1839
|
+
} else if (b.type === "text") {
|
|
1840
|
+
textBlocks.push(b);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
} else if (typeof messageContent === "object" && messageContent !== null && "text" in messageContent) {
|
|
1845
|
+
textBlocks.push(messageContent);
|
|
1846
|
+
}
|
|
1847
|
+
if (block.tool_calls) {
|
|
1848
|
+
for (const tc of block.tool_calls) {
|
|
1849
|
+
toolUseBlocks.push({
|
|
1850
|
+
type: "tool_use",
|
|
1851
|
+
id: tc.id,
|
|
1852
|
+
name: tc.name,
|
|
1853
|
+
input: JSON.parse(tc.arguments)
|
|
1854
|
+
});
|
|
1817
1855
|
}
|
|
1818
1856
|
}
|
|
1819
|
-
}
|
|
1820
|
-
} else if (typeof messageContent === "object" && messageContent !== null && "text" in messageContent) {
|
|
1821
|
-
content.push(messageContent);
|
|
1822
|
-
}
|
|
1823
|
-
if (contentBlocks?.type === "message" && contentBlocks.tool_calls) {
|
|
1824
|
-
for (const tc of contentBlocks.tool_calls) {
|
|
1825
|
-
content.push({
|
|
1826
|
-
type: "tool_use",
|
|
1827
|
-
id: tc.id,
|
|
1828
|
-
name: tc.name,
|
|
1829
|
-
input: JSON.parse(tc.arguments)
|
|
1830
|
-
});
|
|
1831
1857
|
}
|
|
1832
1858
|
}
|
|
1859
|
+
const content = [
|
|
1860
|
+
...thinkingBlocks,
|
|
1861
|
+
...textBlocks,
|
|
1862
|
+
...toolUseBlocks
|
|
1863
|
+
];
|
|
1833
1864
|
return {
|
|
1834
1865
|
id: ulxResponse.id,
|
|
1835
1866
|
type: "message",
|
|
@@ -2089,18 +2120,19 @@ class OpenAIResponsesClientAdapter {
|
|
|
2089
2120
|
}
|
|
2090
2121
|
fromUlx(ulxResponse, ulxRequest) {
|
|
2091
2122
|
const outputBlocks = ulxResponse.output;
|
|
2092
|
-
const output = [];
|
|
2093
2123
|
const textParts = [];
|
|
2094
2124
|
const createdAt = Math.floor(Date.now() / 1000);
|
|
2095
2125
|
let messageIndex = 0;
|
|
2096
2126
|
let functionCallIndex = 0;
|
|
2097
2127
|
let reasoningIndex = 0;
|
|
2128
|
+
const collectedReasoning = [];
|
|
2129
|
+
const collectedMessages = [];
|
|
2130
|
+
const collectedFunctionCalls = [];
|
|
2098
2131
|
for (const block of outputBlocks) {
|
|
2099
2132
|
if (block.type === "message") {
|
|
2100
2133
|
const messageId = `msg_${ulxResponse.id}_${messageIndex++}`;
|
|
2101
2134
|
const status = block.status === "incomplete" ? "incomplete" : "completed";
|
|
2102
2135
|
const content = [];
|
|
2103
|
-
const reasoningContent = [];
|
|
2104
2136
|
for (const entry of block.content) {
|
|
2105
2137
|
if (entry.type === "text") {
|
|
2106
2138
|
const text = entry.text ?? "";
|
|
@@ -2119,22 +2151,16 @@ class OpenAIResponsesClientAdapter {
|
|
|
2119
2151
|
}
|
|
2120
2152
|
content.push({ type: "output_text", text, annotations: [] });
|
|
2121
2153
|
} else if (entry.type === "reasoning") {
|
|
2122
|
-
|
|
2123
|
-
type: "
|
|
2124
|
-
|
|
2154
|
+
collectedReasoning.push({
|
|
2155
|
+
type: "reasoning",
|
|
2156
|
+
id: `rsn_${ulxResponse.id}_${reasoningIndex++}`,
|
|
2157
|
+
status: "completed",
|
|
2158
|
+
content: [{ type: "reasoning_text", text: entry.text ?? "" }],
|
|
2159
|
+
summary: []
|
|
2125
2160
|
});
|
|
2126
2161
|
}
|
|
2127
2162
|
}
|
|
2128
|
-
|
|
2129
|
-
output.push({
|
|
2130
|
-
type: "reasoning",
|
|
2131
|
-
id: `rsn_${ulxResponse.id}_${reasoningIndex++}`,
|
|
2132
|
-
status: "completed",
|
|
2133
|
-
content: reasoningContent,
|
|
2134
|
-
summary: []
|
|
2135
|
-
});
|
|
2136
|
-
}
|
|
2137
|
-
output.push({
|
|
2163
|
+
collectedMessages.push({
|
|
2138
2164
|
type: "message",
|
|
2139
2165
|
id: messageId,
|
|
2140
2166
|
role: "assistant",
|
|
@@ -2143,7 +2169,7 @@ class OpenAIResponsesClientAdapter {
|
|
|
2143
2169
|
});
|
|
2144
2170
|
if (block.tool_calls) {
|
|
2145
2171
|
for (const call of block.tool_calls) {
|
|
2146
|
-
|
|
2172
|
+
collectedFunctionCalls.push({
|
|
2147
2173
|
type: "function_call",
|
|
2148
2174
|
id: `fc_${ulxResponse.id}_${functionCallIndex++}`,
|
|
2149
2175
|
call_id: call.id,
|
|
@@ -2154,7 +2180,7 @@ class OpenAIResponsesClientAdapter {
|
|
|
2154
2180
|
}
|
|
2155
2181
|
}
|
|
2156
2182
|
} else if (block.type === "tool_call") {
|
|
2157
|
-
|
|
2183
|
+
collectedFunctionCalls.push({
|
|
2158
2184
|
type: "function_call",
|
|
2159
2185
|
id: `fc_${ulxResponse.id}_${functionCallIndex++}`,
|
|
2160
2186
|
call_id: block.call_id,
|
|
@@ -2163,7 +2189,7 @@ class OpenAIResponsesClientAdapter {
|
|
|
2163
2189
|
status: block.status === "pending" ? "in_progress" : "completed"
|
|
2164
2190
|
});
|
|
2165
2191
|
} else if (block.type === "reasoning") {
|
|
2166
|
-
|
|
2192
|
+
collectedReasoning.push({
|
|
2167
2193
|
type: "reasoning",
|
|
2168
2194
|
id: `rsn_${ulxResponse.id}_${reasoningIndex++}`,
|
|
2169
2195
|
status: "completed",
|
|
@@ -2178,6 +2204,11 @@ class OpenAIResponsesClientAdapter {
|
|
|
2178
2204
|
});
|
|
2179
2205
|
}
|
|
2180
2206
|
}
|
|
2207
|
+
const output = [
|
|
2208
|
+
...collectedReasoning,
|
|
2209
|
+
...collectedMessages,
|
|
2210
|
+
...collectedFunctionCalls
|
|
2211
|
+
];
|
|
2181
2212
|
const inputTokens = ulxResponse.usage?.input_tokens ?? 0;
|
|
2182
2213
|
const outputTokens = ulxResponse.usage?.output_tokens ?? 0;
|
|
2183
2214
|
const totalTokens = ulxResponse.usage?.total_tokens ?? inputTokens + outputTokens;
|
|
@@ -2387,7 +2418,9 @@ function createCapturingFetch(originalFetch = globalThis.fetch) {
|
|
|
2387
2418
|
// src/core/providers/anthropic.ts
|
|
2388
2419
|
var AnthropicContentSchema = z6.object({
|
|
2389
2420
|
type: z6.string(),
|
|
2390
|
-
text: z6.string().optional()
|
|
2421
|
+
text: z6.string().optional(),
|
|
2422
|
+
thinking: z6.string().optional(),
|
|
2423
|
+
signature: z6.string().optional()
|
|
2391
2424
|
}).passthrough();
|
|
2392
2425
|
var AnthropicResponseSchema = z6.object({
|
|
2393
2426
|
id: z6.string(),
|
|
@@ -2428,6 +2461,13 @@ function toAnthropicContent(blocks) {
|
|
|
2428
2461
|
return blocks.map((entry) => {
|
|
2429
2462
|
if (entry.type === "text")
|
|
2430
2463
|
return { type: "text", text: entry.text ?? "" };
|
|
2464
|
+
if (entry.type === "reasoning") {
|
|
2465
|
+
return {
|
|
2466
|
+
type: "thinking",
|
|
2467
|
+
thinking: entry.text ?? "",
|
|
2468
|
+
signature: entry.data?.signature
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2431
2471
|
if (entry.type === "image_url") {
|
|
2432
2472
|
if (typeof entry.url === "string") {
|
|
2433
2473
|
const match = entry.url.match(/^data:([^;]+);base64,(.+)$/);
|
|
@@ -2468,7 +2508,8 @@ function anthropicResponseToUlx(body, request) {
|
|
|
2468
2508
|
type: "reasoning",
|
|
2469
2509
|
content: reasoning.map((part) => ({
|
|
2470
2510
|
type: "reasoning",
|
|
2471
|
-
text: part.text ?? ""
|
|
2511
|
+
text: part.thinking ?? part.text ?? "",
|
|
2512
|
+
data: part.signature ? { signature: part.signature } : undefined
|
|
2472
2513
|
})),
|
|
2473
2514
|
summary: []
|
|
2474
2515
|
});
|
|
@@ -2626,7 +2667,7 @@ var TOOL_SECTION_END = "<|tool_calls_section_end|>";
|
|
|
2626
2667
|
function cleanText(text) {
|
|
2627
2668
|
if (!text)
|
|
2628
2669
|
return "";
|
|
2629
|
-
return text.replaceAll("(no content)", "").replace(/\n\s*\n\s*\n+/g, `
|
|
2670
|
+
return text.replaceAll("(no content)", "").replace(/<tool_call>[a-zA-Z0-9_:-]+/g, "").replace(/\n\s*\n\s*\n+/g, `
|
|
2630
2671
|
|
|
2631
2672
|
`).trim();
|
|
2632
2673
|
}
|
|
@@ -2752,6 +2793,18 @@ function fixKimiResponse(response, request) {
|
|
|
2752
2793
|
const message = choice.message !== undefined && isJsonObject(choice.message) ? choice.message : choice.message = {};
|
|
2753
2794
|
const rawToolCalls = message.tool_calls;
|
|
2754
2795
|
let aggregatedToolCalls = Array.isArray(rawToolCalls) ? [...rawToolCalls] : [];
|
|
2796
|
+
if (typeof message.reasoning === "string" && !message.reasoning_content) {
|
|
2797
|
+
message.reasoning_content = message.reasoning;
|
|
2798
|
+
}
|
|
2799
|
+
if (Array.isArray(message.reasoning_details) && !message.reasoning_content) {
|
|
2800
|
+
const details = message.reasoning_details;
|
|
2801
|
+
const text = details.filter((d) => d.type === "reasoning.text" && typeof d.text === "string").map((d) => d.text).join(`
|
|
2802
|
+
|
|
2803
|
+
`);
|
|
2804
|
+
if (text) {
|
|
2805
|
+
message.reasoning_content = text;
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2755
2808
|
if (typeof message.reasoning_content === "string") {
|
|
2756
2809
|
const original = message.reasoning_content;
|
|
2757
2810
|
const { cleanedText, extracted } = extractToolCallSections(original);
|
|
@@ -2953,32 +3006,31 @@ function toOpenAITool(tool) {
|
|
|
2953
3006
|
}
|
|
2954
3007
|
function toOpenAIMessages(messages) {
|
|
2955
3008
|
return messages.map((msg) => {
|
|
3009
|
+
const reasoningBlocks = msg.content.filter((c) => c.type === "reasoning");
|
|
3010
|
+
const nonReasoningBlocks = msg.content.filter((c) => c.type !== "reasoning");
|
|
3011
|
+
const res = {
|
|
3012
|
+
role: msg.role === "assistant" ? "assistant" : msg.role,
|
|
3013
|
+
content: toOpenAIContent(nonReasoningBlocks)
|
|
3014
|
+
};
|
|
3015
|
+
if (reasoningBlocks.length > 0) {
|
|
3016
|
+
res.reasoning_content = reasoningBlocks.map((b) => b.text).join(`
|
|
3017
|
+
|
|
3018
|
+
`);
|
|
3019
|
+
}
|
|
2956
3020
|
if (msg.role === "tool") {
|
|
2957
|
-
|
|
2958
|
-
role: "tool",
|
|
2959
|
-
tool_call_id: msg.tool_call_id,
|
|
2960
|
-
content: toOpenAIContent(msg.content)
|
|
2961
|
-
};
|
|
3021
|
+
res.tool_call_id = msg.tool_call_id;
|
|
2962
3022
|
}
|
|
2963
3023
|
if (msg.tool_calls) {
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
arguments: call.arguments
|
|
2973
|
-
}
|
|
2974
|
-
}))
|
|
2975
|
-
};
|
|
3024
|
+
res.tool_calls = msg.tool_calls.map((call) => ({
|
|
3025
|
+
id: call.id,
|
|
3026
|
+
type: "function",
|
|
3027
|
+
function: {
|
|
3028
|
+
name: call.name,
|
|
3029
|
+
arguments: call.arguments
|
|
3030
|
+
}
|
|
3031
|
+
}));
|
|
2976
3032
|
}
|
|
2977
|
-
return
|
|
2978
|
-
role: msg.role === "assistant" ? "assistant" : msg.role,
|
|
2979
|
-
content: toOpenAIContent(msg.content),
|
|
2980
|
-
name: undefined
|
|
2981
|
-
};
|
|
3033
|
+
return res;
|
|
2982
3034
|
});
|
|
2983
3035
|
}
|
|
2984
3036
|
function normalizeOpenAIProviderResponse(payload, request) {
|
|
@@ -3817,13 +3869,53 @@ class VertexProviderAdapter {
|
|
|
3817
3869
|
toVertexTools(tools) {
|
|
3818
3870
|
if (!tools?.length)
|
|
3819
3871
|
return;
|
|
3872
|
+
function flattenJsonSchema(schema) {
|
|
3873
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
3874
|
+
return schema;
|
|
3875
|
+
}
|
|
3876
|
+
const obj = { ...schema };
|
|
3877
|
+
const definitions = obj.definitions;
|
|
3878
|
+
delete obj.definitions;
|
|
3879
|
+
function resolveRef(target, path4) {
|
|
3880
|
+
if (!target || typeof target !== "object" || Array.isArray(target)) {
|
|
3881
|
+
return target;
|
|
3882
|
+
}
|
|
3883
|
+
const copy = { ...target };
|
|
3884
|
+
for (const key in copy) {
|
|
3885
|
+
const value = copy[key];
|
|
3886
|
+
if (key === "$ref" && typeof value === "string" && value.startsWith("#/definitions/")) {
|
|
3887
|
+
const defName = value.slice(14);
|
|
3888
|
+
const definition = definitions?.[defName];
|
|
3889
|
+
if (definition) {
|
|
3890
|
+
delete copy.$ref;
|
|
3891
|
+
const resolved = resolveRef(definition, [...path4, defName]);
|
|
3892
|
+
if (resolved && typeof resolved === "object" && !Array.isArray(resolved)) {
|
|
3893
|
+
Object.assign(copy, resolved);
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
} else if (key === "$ref" && typeof value === "string") {
|
|
3897
|
+
delete copy.$ref;
|
|
3898
|
+
} else {
|
|
3899
|
+
copy[key] = resolveRef(value, path4);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
return copy;
|
|
3903
|
+
}
|
|
3904
|
+
return resolveRef(obj, []);
|
|
3905
|
+
}
|
|
3820
3906
|
return [
|
|
3821
3907
|
{
|
|
3822
|
-
functionDeclarations: tools.map((tool) =>
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3908
|
+
functionDeclarations: tools.map((tool) => {
|
|
3909
|
+
let parameters = tool.parameters;
|
|
3910
|
+
if (parameters && typeof parameters === "object" && !Array.isArray(parameters)) {
|
|
3911
|
+
parameters = flattenJsonSchema(parameters);
|
|
3912
|
+
}
|
|
3913
|
+
return {
|
|
3914
|
+
name: tool.name,
|
|
3915
|
+
description: tool.description,
|
|
3916
|
+
parameters
|
|
3917
|
+
};
|
|
3918
|
+
})
|
|
3827
3919
|
}
|
|
3828
3920
|
];
|
|
3829
3921
|
}
|
|
@@ -5471,16 +5563,16 @@ async function createLiveStoreRuntime(options) {
|
|
|
5471
5563
|
const clauses = [];
|
|
5472
5564
|
const params = { limit };
|
|
5473
5565
|
if (checkpoint.timestamp) {
|
|
5474
|
-
clauses.push("(timestamp
|
|
5566
|
+
clauses.push("(timestamp < $ts OR (timestamp = $ts AND numeric_id < $id))");
|
|
5475
5567
|
params.ts = checkpoint.timestamp;
|
|
5476
|
-
params.id = checkpoint.id ??
|
|
5568
|
+
params.id = checkpoint.id ?? Number.MAX_SAFE_INTEGER;
|
|
5477
5569
|
}
|
|
5478
5570
|
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
5479
5571
|
const rows = store.query({
|
|
5480
5572
|
query: `
|
|
5481
5573
|
SELECT * FROM logs
|
|
5482
5574
|
${where}
|
|
5483
|
-
ORDER BY timestamp, numeric_id
|
|
5575
|
+
ORDER BY timestamp DESC, numeric_id DESC
|
|
5484
5576
|
LIMIT $limit
|
|
5485
5577
|
`,
|
|
5486
5578
|
bindValues: params
|
|
@@ -5501,6 +5593,29 @@ async function createLiveStoreRuntime(options) {
|
|
|
5501
5593
|
return;
|
|
5502
5594
|
return { timestamp: latest.timestamp, id: latest.numeric_id };
|
|
5503
5595
|
},
|
|
5596
|
+
async trim(maxRecords) {
|
|
5597
|
+
const countResult = store.query({
|
|
5598
|
+
query: `SELECT COUNT(*) as count FROM logs`,
|
|
5599
|
+
bindValues: {}
|
|
5600
|
+
});
|
|
5601
|
+
const currentCount = countResult[0]?.count ?? 0;
|
|
5602
|
+
if (currentCount <= maxRecords)
|
|
5603
|
+
return 0;
|
|
5604
|
+
const toDelete = currentCount - maxRecords;
|
|
5605
|
+
const deleteResult = store.query({
|
|
5606
|
+
query: `
|
|
5607
|
+
DELETE FROM logs
|
|
5608
|
+
WHERE id IN (
|
|
5609
|
+
SELECT id FROM logs
|
|
5610
|
+
ORDER BY timestamp ASC, numeric_id ASC
|
|
5611
|
+
LIMIT @limit
|
|
5612
|
+
)
|
|
5613
|
+
RETURNING COUNT(*) as deleted
|
|
5614
|
+
`,
|
|
5615
|
+
bindValues: { limit: toDelete }
|
|
5616
|
+
});
|
|
5617
|
+
return deleteResult[0]?.deleted ?? 0;
|
|
5618
|
+
},
|
|
5504
5619
|
async close() {
|
|
5505
5620
|
await store.shutdown();
|
|
5506
5621
|
}
|
|
@@ -5548,6 +5663,21 @@ async function createServer(config) {
|
|
|
5548
5663
|
batchSize: config.livestore.batchSize
|
|
5549
5664
|
});
|
|
5550
5665
|
logger.info({ seeded }, "Seeded LiveStore log mirror");
|
|
5666
|
+
if (config.livestore.maxRecords && config.livestore.maxRecords > 0) {
|
|
5667
|
+
const trimInterval = setInterval(async () => {
|
|
5668
|
+
try {
|
|
5669
|
+
const deleted = await liveStoreRuntime.trim(config.livestore.maxRecords);
|
|
5670
|
+
if (deleted > 0) {
|
|
5671
|
+
logger.debug({ deleted, maxRecords: config.livestore.maxRecords }, "LiveStore trimmed old records");
|
|
5672
|
+
}
|
|
5673
|
+
} catch (error) {
|
|
5674
|
+
logger.error({ err: error }, "Failed to trim LiveStore records");
|
|
5675
|
+
}
|
|
5676
|
+
}, 30000);
|
|
5677
|
+
server.addHook("onClose", async () => {
|
|
5678
|
+
clearInterval(trimInterval);
|
|
5679
|
+
});
|
|
5680
|
+
}
|
|
5551
5681
|
server.addHook("onClose", async () => {
|
|
5552
5682
|
await liveStoreRuntime.close();
|
|
5553
5683
|
});
|
|
@@ -5774,6 +5904,31 @@ async function createServer(config) {
|
|
|
5774
5904
|
timestamp: meta.timestamp
|
|
5775
5905
|
});
|
|
5776
5906
|
});
|
|
5907
|
+
server.get("/api/config", (_req, reply) => {
|
|
5908
|
+
reply.send({
|
|
5909
|
+
blobRoot: config.logging.blobRoot
|
|
5910
|
+
});
|
|
5911
|
+
});
|
|
5912
|
+
server.get("/api/logs/:id/path", (req, reply) => {
|
|
5913
|
+
const { id } = req.params;
|
|
5914
|
+
const meta = logStore.readMetadata(Number(id));
|
|
5915
|
+
if (!meta) {
|
|
5916
|
+
reply.status(404).send({ error: { message: "Log not found" } });
|
|
5917
|
+
return;
|
|
5918
|
+
}
|
|
5919
|
+
const date = new Date(meta.timestamp);
|
|
5920
|
+
const year = date.getUTCFullYear();
|
|
5921
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
5922
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
5923
|
+
const dirPath = `${config.logging.blobRoot}/${year}/${month}/${day}/${meta.request_id}/`;
|
|
5924
|
+
reply.send({
|
|
5925
|
+
directory: dirPath,
|
|
5926
|
+
request: meta.request_path ? `${config.logging.blobRoot}/${meta.request_path}` : null,
|
|
5927
|
+
response: meta.response_path ? `${config.logging.blobRoot}/${meta.response_path}` : null,
|
|
5928
|
+
providerRequest: meta.provider_request_path ? `${config.logging.blobRoot}/${meta.provider_request_path}` : null,
|
|
5929
|
+
providerResponse: meta.provider_response_path ? `${config.logging.blobRoot}/${meta.provider_response_path}` : null
|
|
5930
|
+
});
|
|
5931
|
+
});
|
|
5777
5932
|
return server;
|
|
5778
5933
|
}
|
|
5779
5934
|
async function handleRequest(req, reply, body, modelRegistry, pipeline, logStore, liveStoreRuntime, config, options) {
|
|
@@ -6045,5 +6200,5 @@ async function bootstrap() {
|
|
|
6045
6200
|
}
|
|
6046
6201
|
bootstrap();
|
|
6047
6202
|
|
|
6048
|
-
//# debugId=
|
|
6203
|
+
//# debugId=BB1E97A73BB51B4864756E2164756E21
|
|
6049
6204
|
//# sourceMappingURL=index.js.map
|