lynkr 7.2.5 → 8.0.1
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 +3 -3
- package/config/model-tiers.json +89 -0
- package/install.sh +6 -1
- package/package.json +4 -2
- package/scripts/setup.js +0 -1
- package/src/agents/executor.js +14 -6
- package/src/api/middleware/session.js +15 -2
- package/src/api/openai-router.js +162 -37
- package/src/api/providers-handler.js +15 -1
- package/src/api/router.js +107 -2
- package/src/budget/index.js +4 -3
- package/src/clients/databricks.js +431 -234
- package/src/clients/gpt-utils.js +181 -0
- package/src/clients/ollama-utils.js +66 -140
- package/src/clients/routing.js +0 -1
- package/src/clients/standard-tools.js +99 -3
- package/src/config/index.js +133 -35
- package/src/context/toon.js +173 -0
- package/src/logger/index.js +23 -0
- package/src/orchestrator/index.js +688 -213
- package/src/routing/agentic-detector.js +320 -0
- package/src/routing/complexity-analyzer.js +202 -2
- package/src/routing/cost-optimizer.js +305 -0
- package/src/routing/index.js +168 -159
- package/src/routing/model-tiers.js +365 -0
- package/src/server.js +4 -14
- package/src/sessions/cleanup.js +3 -3
- package/src/sessions/record.js +10 -1
- package/src/sessions/store.js +7 -2
- package/src/tools/agent-task.js +48 -1
- package/src/tools/index.js +19 -2
- package/src/tools/lazy-loader.js +7 -0
- package/src/tools/tinyfish.js +358 -0
- package/src/tools/truncate.js +1 -0
- package/.github/FUNDING.yml +0 -15
- package/.github/workflows/README.md +0 -215
- package/.github/workflows/ci.yml +0 -69
- package/.github/workflows/index.yml +0 -62
- package/.github/workflows/web-tools-tests.yml +0 -56
- package/CITATIONS.bib +0 -6
- package/CLAWROUTER_ROUTING_PLAN.md +0 -910
- package/DEPLOYMENT.md +0 -1001
- package/LYNKR-TUI-PLAN.md +0 -984
- package/PERFORMANCE-REPORT.md +0 -866
- package/PLAN-per-client-model-routing.md +0 -252
- package/ROUTER_COMPARISON.md +0 -173
- package/TIER_ROUTING_PLAN.md +0 -771
- package/docs/42642f749da6234f41b6b425c3bb07c9.txt +0 -1
- package/docs/BingSiteAuth.xml +0 -4
- package/docs/docs-style.css +0 -478
- package/docs/docs.html +0 -197
- package/docs/google5be250e608e6da39.html +0 -1
- package/docs/index.html +0 -577
- package/docs/index.md +0 -577
- package/docs/robots.txt +0 -4
- package/docs/sitemap.xml +0 -44
- package/docs/style.css +0 -1223
- package/documentation/README.md +0 -100
- package/documentation/api.md +0 -806
- package/documentation/claude-code-cli.md +0 -672
- package/documentation/codex-cli.md +0 -397
- package/documentation/contributing.md +0 -571
- package/documentation/cursor-integration.md +0 -731
- package/documentation/docker.md +0 -867
- package/documentation/embeddings.md +0 -760
- package/documentation/faq.md +0 -659
- package/documentation/features.md +0 -396
- package/documentation/headroom.md +0 -519
- package/documentation/installation.md +0 -706
- package/documentation/memory-system.md +0 -476
- package/documentation/production.md +0 -601
- package/documentation/providers.md +0 -906
- package/documentation/testing.md +0 -629
- package/documentation/token-optimization.md +0 -323
- package/documentation/tools.md +0 -697
- package/documentation/troubleshooting.md +0 -893
- package/final-test.js +0 -33
- package/headroom-sidecar/config.py +0 -93
- package/headroom-sidecar/requirements.txt +0 -14
- package/headroom-sidecar/server.py +0 -451
- package/monitor-agents.sh +0 -31
- package/scripts/audit-log-reader.js +0 -399
- package/scripts/compact-dictionary.js +0 -204
- package/scripts/test-deduplication.js +0 -448
- package/src/db/database.sqlite +0 -0
- package/test/README.md +0 -212
- package/test/azure-openai-config.test.js +0 -204
- package/test/azure-openai-error-resilience.test.js +0 -238
- package/test/azure-openai-format-conversion.test.js +0 -354
- package/test/azure-openai-integration.test.js +0 -281
- package/test/azure-openai-routing.test.js +0 -177
- package/test/azure-openai-streaming.test.js +0 -171
- package/test/bedrock-integration.test.js +0 -471
- package/test/comprehensive-test-suite.js +0 -928
- package/test/config-validation.test.js +0 -207
- package/test/cursor-integration.test.js +0 -484
- package/test/format-conversion.test.js +0 -578
- package/test/hybrid-routing-integration.test.js +0 -254
- package/test/hybrid-routing-performance.test.js +0 -418
- package/test/llamacpp-integration.test.js +0 -863
- package/test/lmstudio-integration.test.js +0 -335
- package/test/memory/extractor.test.js +0 -398
- package/test/memory/retriever.test.js +0 -613
- package/test/memory/retriever.test.js.bak +0 -585
- package/test/memory/search.test.js +0 -537
- package/test/memory/search.test.js.bak +0 -389
- package/test/memory/store.test.js +0 -344
- package/test/memory/store.test.js.bak +0 -312
- package/test/memory/surprise.test.js +0 -300
- package/test/memory-performance.test.js +0 -472
- package/test/openai-integration.test.js +0 -686
- package/test/openrouter-error-resilience.test.js +0 -418
- package/test/passthrough-mode.test.js +0 -385
- package/test/performance-benchmark.js +0 -351
- package/test/performance-tests.js +0 -528
- package/test/routing.test.js +0 -219
- package/test/web-tools.test.js +0 -329
- package/test-agents-simple.js +0 -43
- package/test-cli-connection.sh +0 -33
- package/test-learning-unit.js +0 -126
- package/test-learning.js +0 -112
- package/test-parallel-agents.sh +0 -124
- package/test-parallel-direct.js +0 -155
- package/test-subagents.sh +0 -117
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
const assert = require("assert");
|
|
2
|
-
const { describe, it, beforeEach, afterEach } = require("node:test");
|
|
3
|
-
|
|
4
|
-
describe("Azure OpenAI Routing Tests", () => {
|
|
5
|
-
let routing;
|
|
6
|
-
let originalConfig;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
// Clear module cache
|
|
10
|
-
delete require.cache[require.resolve("../src/config")];
|
|
11
|
-
delete require.cache[require.resolve("../src/clients/routing")];
|
|
12
|
-
|
|
13
|
-
// Store original config
|
|
14
|
-
originalConfig = { ...process.env };
|
|
15
|
-
|
|
16
|
-
// Clean OpenRouter config from previous tests
|
|
17
|
-
delete process.env.OPENROUTER_API_KEY;
|
|
18
|
-
|
|
19
|
-
// Base config for routing tests
|
|
20
|
-
process.env.MODEL_PROVIDER = "databricks"; // Set default to avoid validation errors
|
|
21
|
-
process.env.DATABRICKS_API_KEY = "test-key";
|
|
22
|
-
process.env.DATABRICKS_API_BASE = "http://test.com";
|
|
23
|
-
|
|
24
|
-
// Explicitly set valid fallback to override any local .env pollution (e.g. lmstudio)
|
|
25
|
-
process.env.FALLBACK_PROVIDER = "databricks";
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
afterEach(() => {
|
|
29
|
-
// Restore original environment
|
|
30
|
-
process.env = originalConfig;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe("Primary Provider Routing", () => {
|
|
34
|
-
it("should route to azure-openai when set as MODEL_PROVIDER", () => {
|
|
35
|
-
process.env.MODEL_PROVIDER = "azure-openai";
|
|
36
|
-
process.env.AZURE_OPENAI_ENDPOINT = "https://test.openai.azure.com";
|
|
37
|
-
process.env.AZURE_OPENAI_API_KEY = "test-key";
|
|
38
|
-
process.env.PREFER_OLLAMA = "false";
|
|
39
|
-
|
|
40
|
-
routing = require("../src/clients/routing");
|
|
41
|
-
|
|
42
|
-
const provider = routing.determineProvider({ tools: [] });
|
|
43
|
-
|
|
44
|
-
assert.strictEqual(provider, "azure-openai");
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe("Hybrid Routing with Azure OpenAI", () => {
|
|
49
|
-
it("should route moderate tool requests to azure-openai when available", () => {
|
|
50
|
-
// Explicitly unset OpenRouter to ensure it's not available
|
|
51
|
-
// Set to empty string instead of delete to prevent dotenv from reloading it
|
|
52
|
-
process.env.OPENROUTER_API_KEY = "";
|
|
53
|
-
|
|
54
|
-
process.env.PREFER_OLLAMA = "true";
|
|
55
|
-
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
56
|
-
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
57
|
-
process.env.FALLBACK_ENABLED = "true";
|
|
58
|
-
process.env.OLLAMA_MAX_TOOLS_FOR_ROUTING = "3";
|
|
59
|
-
process.env.OPENROUTER_MAX_TOOLS_FOR_ROUTING = "15";
|
|
60
|
-
process.env.AZURE_OPENAI_ENDPOINT = "https://test.openai.azure.com";
|
|
61
|
-
process.env.AZURE_OPENAI_API_KEY = "test-key";
|
|
62
|
-
|
|
63
|
-
// Clear cache after env setup
|
|
64
|
-
delete require.cache[require.resolve("../src/config/index.js")];
|
|
65
|
-
delete require.cache[require.resolve("../src/clients/routing")];
|
|
66
|
-
delete require.cache[require.resolve("../src/routing/index.js")];
|
|
67
|
-
|
|
68
|
-
routing = require("../src/clients/routing");
|
|
69
|
-
|
|
70
|
-
// 5 tools: more than Ollama threshold (3), less than OpenRouter threshold (15)
|
|
71
|
-
const provider = routing.determineProvider({
|
|
72
|
-
tools: [{}, {}, {}, {}, {}]
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
assert.strictEqual(provider, "azure-openai");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("should prefer OpenRouter over Azure OpenAI when both configured", () => {
|
|
79
|
-
process.env.PREFER_OLLAMA = "true";
|
|
80
|
-
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
81
|
-
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
82
|
-
process.env.FALLBACK_ENABLED = "true";
|
|
83
|
-
process.env.OLLAMA_MAX_TOOLS_FOR_ROUTING = "3";
|
|
84
|
-
process.env.OPENROUTER_MAX_TOOLS_FOR_ROUTING = "15";
|
|
85
|
-
process.env.OPENROUTER_API_KEY = "openrouter-key";
|
|
86
|
-
process.env.AZURE_OPENAI_ENDPOINT = "https://test.openai.azure.com";
|
|
87
|
-
process.env.AZURE_OPENAI_API_KEY = "azure-key";
|
|
88
|
-
|
|
89
|
-
// Clear cache after env setup
|
|
90
|
-
delete require.cache[require.resolve("../src/config/index.js")];
|
|
91
|
-
delete require.cache[require.resolve("../src/clients/routing")];
|
|
92
|
-
delete require.cache[require.resolve("../src/routing/index.js")];
|
|
93
|
-
|
|
94
|
-
routing = require("../src/clients/routing");
|
|
95
|
-
|
|
96
|
-
// 5 tools: should prefer OpenRouter
|
|
97
|
-
const provider = routing.determineProvider({
|
|
98
|
-
tools: [{}, {}, {}, {}, {}]
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
assert.strictEqual(provider, "openrouter");
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("should route simple requests to Ollama even when Azure OpenAI configured", () => {
|
|
105
|
-
process.env.PREFER_OLLAMA = "true";
|
|
106
|
-
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
107
|
-
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
108
|
-
process.env.FALLBACK_ENABLED = "true";
|
|
109
|
-
process.env.OLLAMA_MAX_TOOLS_FOR_ROUTING = "3";
|
|
110
|
-
process.env.AZURE_OPENAI_ENDPOINT = "https://test.openai.azure.com";
|
|
111
|
-
process.env.AZURE_OPENAI_API_KEY = "test-key";
|
|
112
|
-
|
|
113
|
-
// Clear cache after env setup
|
|
114
|
-
delete require.cache[require.resolve("../src/config/index.js")];
|
|
115
|
-
delete require.cache[require.resolve("../src/clients/routing")];
|
|
116
|
-
delete require.cache[require.resolve("../src/routing/index.js")];
|
|
117
|
-
|
|
118
|
-
routing = require("../src/clients/routing");
|
|
119
|
-
|
|
120
|
-
// 2 tools: under Ollama threshold
|
|
121
|
-
const provider = routing.determineProvider({
|
|
122
|
-
tools: [{}, {}]
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
assert.strictEqual(provider, "ollama");
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe("Fallback Configuration", () => {
|
|
130
|
-
it("should support azure-openai as fallback provider", () => {
|
|
131
|
-
process.env.PREFER_OLLAMA = "true";
|
|
132
|
-
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
133
|
-
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
134
|
-
process.env.FALLBACK_ENABLED = "true";
|
|
135
|
-
process.env.FALLBACK_PROVIDER = "azure-openai";
|
|
136
|
-
process.env.AZURE_OPENAI_ENDPOINT = "https://test.openai.azure.com";
|
|
137
|
-
process.env.AZURE_OPENAI_API_KEY = "test-key";
|
|
138
|
-
|
|
139
|
-
// Clear cache after env setup
|
|
140
|
-
delete require.cache[require.resolve("../src/config/index.js")];
|
|
141
|
-
delete require.cache[require.resolve("../src/clients/routing")];
|
|
142
|
-
delete require.cache[require.resolve("../src/routing/index.js")];
|
|
143
|
-
|
|
144
|
-
routing = require("../src/clients/routing");
|
|
145
|
-
|
|
146
|
-
const fallbackProvider = routing.getFallbackProvider();
|
|
147
|
-
|
|
148
|
-
assert.strictEqual(fallbackProvider, "azure-openai");
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("should return true for fallback enabled", () => {
|
|
152
|
-
process.env.FALLBACK_ENABLED = "true";
|
|
153
|
-
|
|
154
|
-
// Clear cache after env setup
|
|
155
|
-
delete require.cache[require.resolve("../src/config/index.js")];
|
|
156
|
-
delete require.cache[require.resolve("../src/clients/routing")];
|
|
157
|
-
delete require.cache[require.resolve("../src/routing/index.js")];
|
|
158
|
-
|
|
159
|
-
routing = require("../src/clients/routing");
|
|
160
|
-
|
|
161
|
-
assert.strictEqual(routing.isFallbackEnabled(), true);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("should return false when fallback disabled", () => {
|
|
165
|
-
process.env.FALLBACK_ENABLED = "false";
|
|
166
|
-
|
|
167
|
-
// Clear cache after env setup
|
|
168
|
-
delete require.cache[require.resolve("../src/config/index.js")];
|
|
169
|
-
delete require.cache[require.resolve("../src/clients/routing")];
|
|
170
|
-
delete require.cache[require.resolve("../src/routing/index.js")];
|
|
171
|
-
|
|
172
|
-
routing = require("../src/clients/routing");
|
|
173
|
-
|
|
174
|
-
assert.strictEqual(routing.isFallbackEnabled(), false);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
});
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
const assert = require("assert");
|
|
2
|
-
const { describe, it } = require("node:test");
|
|
3
|
-
|
|
4
|
-
describe("Azure OpenAI Streaming Tests", () => {
|
|
5
|
-
describe("Streaming Response Structure", () => {
|
|
6
|
-
it("should recognize Azure OpenAI SSE streaming format", () => {
|
|
7
|
-
const streamChunk = {
|
|
8
|
-
id: "chatcmpl-123",
|
|
9
|
-
object: "chat.completion.chunk",
|
|
10
|
-
created: 1677652288,
|
|
11
|
-
model: "gpt-4o",
|
|
12
|
-
choices: [
|
|
13
|
-
{
|
|
14
|
-
index: 0,
|
|
15
|
-
delta: {
|
|
16
|
-
content: "Hello"
|
|
17
|
-
},
|
|
18
|
-
finish_reason: null
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
assert.strictEqual(streamChunk.object, "chat.completion.chunk");
|
|
24
|
-
assert.ok(streamChunk.choices[0].delta);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("should handle streaming delta with tool_calls", () => {
|
|
28
|
-
const streamChunk = {
|
|
29
|
-
id: "chatcmpl-123",
|
|
30
|
-
object: "chat.completion.chunk",
|
|
31
|
-
model: "gpt-4o",
|
|
32
|
-
choices: [
|
|
33
|
-
{
|
|
34
|
-
index: 0,
|
|
35
|
-
delta: {
|
|
36
|
-
tool_calls: [
|
|
37
|
-
{
|
|
38
|
-
index: 0,
|
|
39
|
-
id: "call_abc123",
|
|
40
|
-
type: "function",
|
|
41
|
-
function: {
|
|
42
|
-
name: "Read",
|
|
43
|
-
arguments: "{\"file"
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
]
|
|
47
|
-
},
|
|
48
|
-
finish_reason: null
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
assert.ok(streamChunk.choices[0].delta.tool_calls);
|
|
54
|
-
assert.strictEqual(streamChunk.choices[0].delta.tool_calls[0].function.name, "Read");
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("should handle final streaming chunk with finish_reason", () => {
|
|
58
|
-
const finalChunk = {
|
|
59
|
-
id: "chatcmpl-123",
|
|
60
|
-
object: "chat.completion.chunk",
|
|
61
|
-
model: "gpt-4o",
|
|
62
|
-
choices: [
|
|
63
|
-
{
|
|
64
|
-
index: 0,
|
|
65
|
-
delta: {},
|
|
66
|
-
finish_reason: "stop"
|
|
67
|
-
}
|
|
68
|
-
]
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
assert.strictEqual(finalChunk.choices[0].finish_reason, "stop");
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("should handle tool_calls finish_reason in streaming", () => {
|
|
75
|
-
const toolCallFinish = {
|
|
76
|
-
id: "chatcmpl-123",
|
|
77
|
-
object: "chat.completion.chunk",
|
|
78
|
-
model: "gpt-4o",
|
|
79
|
-
choices: [
|
|
80
|
-
{
|
|
81
|
-
index: 0,
|
|
82
|
-
delta: {},
|
|
83
|
-
finish_reason: "tool_calls"
|
|
84
|
-
}
|
|
85
|
-
]
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
assert.strictEqual(toolCallFinish.choices[0].finish_reason, "tool_calls");
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe("Stream Flag in Requests", () => {
|
|
93
|
-
it("should set stream:false by default for buffered requests", () => {
|
|
94
|
-
const requestBody = {
|
|
95
|
-
messages: [{ role: "user", content: "Hello" }],
|
|
96
|
-
temperature: 0.7
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// Default stream value
|
|
100
|
-
const stream = requestBody.stream ?? false;
|
|
101
|
-
|
|
102
|
-
assert.strictEqual(stream, false);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("should set stream:true for streaming requests", () => {
|
|
106
|
-
const requestBody = {
|
|
107
|
-
messages: [{ role: "user", content: "Hello" }],
|
|
108
|
-
stream: true
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
assert.strictEqual(requestBody.stream, true);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
describe("SSE Format Validation", () => {
|
|
116
|
-
it("should recognize SSE event format with data prefix", () => {
|
|
117
|
-
const sseChunk = 'data: {"id":"chatcmpl-123","object":"chat.completion.chunk","choices":[{"delta":{"content":"Hi"}}]}';
|
|
118
|
-
|
|
119
|
-
assert.ok(sseChunk.startsWith("data:"));
|
|
120
|
-
|
|
121
|
-
// Extract JSON after "data: "
|
|
122
|
-
const jsonStr = sseChunk.substring(6);
|
|
123
|
-
const parsed = JSON.parse(jsonStr);
|
|
124
|
-
|
|
125
|
-
assert.strictEqual(parsed.object, "chat.completion.chunk");
|
|
126
|
-
assert.strictEqual(parsed.choices[0].delta.content, "Hi");
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it("should handle SSE done signal", () => {
|
|
130
|
-
const doneSig = "data: [DONE]";
|
|
131
|
-
|
|
132
|
-
assert.strictEqual(doneSig, "data: [DONE]");
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe("Chunk Accumulation", () => {
|
|
137
|
-
it("should accumulate tool arguments across multiple chunks", () => {
|
|
138
|
-
const chunks = [
|
|
139
|
-
{ delta: { tool_calls: [{ index: 0, function: { arguments: "{\"file_" } }] } },
|
|
140
|
-
{ delta: { tool_calls: [{ index: 0, function: { arguments: "path\":" } }] } },
|
|
141
|
-
{ delta: { tool_calls: [{ index: 0, function: { arguments: "\"/test.js" } }] } },
|
|
142
|
-
{ delta: { tool_calls: [{ index: 0, function: { arguments: "\"}" } }] } }
|
|
143
|
-
];
|
|
144
|
-
|
|
145
|
-
let accumulated = "";
|
|
146
|
-
for (const chunk of chunks) {
|
|
147
|
-
accumulated += chunk.delta.tool_calls[0].function.arguments;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
assert.strictEqual(accumulated, '{"file_path":"/test.js"}');
|
|
151
|
-
const parsed = JSON.parse(accumulated);
|
|
152
|
-
assert.strictEqual(parsed.file_path, "/test.js");
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("should accumulate content across multiple chunks", () => {
|
|
156
|
-
const chunks = [
|
|
157
|
-
{ delta: { content: "Hello" } },
|
|
158
|
-
{ delta: { content: ", " } },
|
|
159
|
-
{ delta: { content: "world" } },
|
|
160
|
-
{ delta: { content: "!" } }
|
|
161
|
-
];
|
|
162
|
-
|
|
163
|
-
let accumulated = "";
|
|
164
|
-
for (const chunk of chunks) {
|
|
165
|
-
accumulated += chunk.delta.content;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
assert.strictEqual(accumulated, "Hello, world!");
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
});
|