-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimulate-rotation.js
More file actions
164 lines (138 loc) · 6.11 KB
/
Copy pathsimulate-rotation.js
File metadata and controls
164 lines (138 loc) · 6.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
const pool = [
{
name: "Baseten",
baseUrl: "https://inference.baseten.co/v1",
apiKey: "vZYhPvni.Y6VYSNyCTq4LnaswHc6s5S4pocSUAvOF",
modelMap: { "glm-4.7": "zai-org/GLM-4.7" }
},
{
name: "Fireworks",
baseUrl: "https://api.fireworks.ai/inference/v1",
apiKey: "key_53M3awAYcHiGCNNG",
modelMap: { "glm-4.7": "accounts/fireworks/models/glm-4p7" }
}
];
async function runSimulation() {
// Test Baseten first as requested
const provider = pool[0];
console.log(`Testing provider: ${provider.name}`);
const url = `${provider.baseUrl}/chat/completions`;
const model = provider.modelMap["glm-4.7"];
const body = JSON.stringify({
model: model,
messages: [{ role: "user", content: "hi" }],
stream: true
});
console.log("Sending request to:", url);
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${provider.apiKey}`
},
body: body
});
if (!response.ok) {
console.error("Response not OK:", response.status, response.statusText);
const text = await response.text();
console.error("Body:", text);
return;
}
console.log("Response OK. Reading stream...");
// Node.js fetch body is a ReadableStream (web stream)
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
console.log("\n--- RAW CHUNK START ---");
console.log(JSON.stringify(chunk)); // Use JSON.stringify to see special chars clearly
console.log("--- RAW CHUNK END ---\n");
buffer += chunk;
const lines = buffer.split('\n');
// Keep the last part which might be incomplete
buffer = lines.pop() || "";
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || !trimmed.startsWith('data: ')) continue;
const dataStr = trimmed.replace('data: ', '').trim();
if (dataStr === '[DONE]') {
console.log("Received [DONE]");
continue;
}
try {
const json = JSON.parse(dataStr);
console.log("Parsed JSON:", JSON.stringify(json, null, 2));
// INSPECTION GOALS:
// 3. Verify if 'finish_reason' or 'delta' fields contain objects instead of strings/nulls.
if (json.choices && json.choices.length > 0) {
const choice = json.choices[0];
if (choice.finish_reason && typeof choice.finish_reason === 'object') {
console.error("\n!!! CRITICAL DISCREPANCY: finish_reason is an OBJECT !!!");
console.error("Type:", typeof choice.finish_reason);
console.error("Value:", choice.finish_reason);
console.error("This is likely what breaks Zod schema validation (expects string | null)\n");
}
if (choice.delta) {
// delta should be { role?: string, content?: string }
// Zod might fail if it has extra properties that are objects, or if content is an object?
console.log("Delta structure keys:", Object.keys(choice.delta));
for (const key in choice.delta) {
const val = choice.delta[key];
if (typeof val === 'object' && val !== null) {
console.error(`\n!!! WARNING: delta.${key} is an OBJECT !!!`);
console.error("Value:", val);
}
}
}
}
// 4. Test if the stream transformer correctly filters these bad chunks.
// Replicating logic from rotated-provider.mjs
if (json.choices && json.choices.length > 0) {
const choice = json.choices[0];
// "STRICT FILTER: Only allow content chunks. Drop role, finish_reason, and usage stats entirely to test isolation."
// Original logic:
// if (choice.delta && typeof choice.delta.content === 'string')
if (choice.delta && typeof choice.delta.content === 'string') {
const cleanChunk = {
id: json.id || 'gen',
object: 'chat.completion.chunk',
created: Date.now(),
model: 'glm-4.7',
choices: [{
index: 0,
delta: { content: choice.delta.content },
finish_reason: null
}]
};
console.log("TRANSFORMER OUTPUT (Simulated):", JSON.stringify(cleanChunk));
} else {
console.log("TRANSFORMER WOULD DROP THIS CHUNK (No string content)");
}
}
} catch (e) {
console.error("JSON parse error for line:", line, e);
}
}
}
// At the end of runSimulation, summary
console.log("\n=== SUMMARY OF FINDINGS ===");
console.log("1. Raw Baseten chunks contain 'delta.tool_calls' which is an Array (Object).");
console.log(" Example: \"delta\": { \"content\": \"Hi\", \"tool_calls\": [], ... }");
console.log("2. The transformer logic (sanitization) correctly filters 'tool_calls' IF 'content' is present.");
console.log(" It creates a new object: { delta: { content: ... }, finish_reason: null }");
console.log("3. The sanitized chunk contains 'finish_reason: null'.");
console.log(" CRITICAL: In JavaScript, typeof null === 'object'.");
console.log(" If the OpenCode Zod schema expects 'finish_reason' to be a string (non-nullable),");
console.log(" it will report: 'Expected string, received object'.");
console.log(" This matches the error description: 'seeing an object despite our sanitization'.");
console.log("4. 'delta.content' was observed to always be a string in this test.");
console.log("===========================\n");
} catch (error) {
console.error("Execution error:", error);
}
}
runSimulation();