thebird 1.2.31 → 1.2.32
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/CHANGELOG.md +7 -0
- package/docs/agent-chat.js +43 -47
- package/docs/app.js +1 -14
- package/docs/vendor/thebird-browser.js +18437 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [Unreleased - browser-sdk]
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- `docs/vendor/thebird-browser.js`: thebird `streamGemini`/`generateGemini` bundled for browser via esbuild (712KB, includes @google/genai browser build)
|
|
5
|
+
- `docs/agent-chat.js`: rewritten to use thebird `streamGemini` directly; TOOLS map with `read_file`, `write_file`, `run_command` dispatch to `window.__debug.container` (WebContainer); `window.__debug.agent` live state; removes raw Gemini REST API dependency
|
|
6
|
+
- `docs/app.js`: removed `convertMessages` (now handled internally by thebird); passes raw Anthropic-format messages to `agentGenerate`
|
|
7
|
+
|
|
1
8
|
# Changelog
|
|
2
9
|
|
|
3
10
|
## [Unreleased]
|
package/docs/agent-chat.js
CHANGED
|
@@ -1,55 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
{ name: 'read_file', description: 'Read a file from the filesystem', parameters: { type: 'OBJECT', properties: { path: { type: 'STRING' } }, required: ['path'] } },
|
|
3
|
-
{ name: 'write_file', description: 'Write content to a file', parameters: { type: 'OBJECT', properties: { path: { type: 'STRING' }, content: { type: 'STRING' } }, required: ['path', 'content'] } },
|
|
4
|
-
{ name: 'run_command', description: 'Run a shell command', parameters: { type: 'OBJECT', properties: { command: { type: 'STRING' } }, required: ['command'] } },
|
|
5
|
-
];
|
|
1
|
+
import { streamGemini } from './vendor/thebird-browser.js';
|
|
6
2
|
|
|
7
|
-
const
|
|
8
|
-
read_file:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
const TOOLS = {
|
|
4
|
+
read_file: {
|
|
5
|
+
description: 'Read a file from the filesystem',
|
|
6
|
+
parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
7
|
+
execute: async ({ path }) => {
|
|
8
|
+
const c = window.__debug.container;
|
|
9
|
+
if (!c) throw new Error('container not ready');
|
|
10
|
+
return await c.fs.readFile(path, 'utf-8');
|
|
11
|
+
},
|
|
12
12
|
},
|
|
13
|
-
write_file:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
write_file: {
|
|
14
|
+
description: 'Write content to a file',
|
|
15
|
+
parameters: { type: 'object', properties: { path: { type: 'string' }, content: { type: 'string' } }, required: ['path', 'content'] },
|
|
16
|
+
execute: async ({ path, content }) => {
|
|
17
|
+
const c = window.__debug.container;
|
|
18
|
+
if (!c) throw new Error('container not ready');
|
|
19
|
+
await c.fs.writeFile(path, content);
|
|
20
|
+
return 'written: ' + path;
|
|
21
|
+
},
|
|
18
22
|
},
|
|
19
|
-
run_command:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
run_command: {
|
|
24
|
+
description: 'Run a shell command',
|
|
25
|
+
parameters: { type: 'object', properties: { command: { type: 'string' }, cwd: { type: 'string' } }, required: ['command'] },
|
|
26
|
+
execute: async ({ command, cwd }) => {
|
|
27
|
+
const c = window.__debug.container;
|
|
28
|
+
if (!c) throw new Error('container not ready');
|
|
29
|
+
const proc = await c.spawn('sh', ['-c', command], cwd ? { cwd } : undefined);
|
|
30
|
+
let out = '';
|
|
31
|
+
await proc.output.pipeTo(new WritableStream({ write: d => { out += d; } }));
|
|
32
|
+
await proc.exit;
|
|
33
|
+
return out || '(no output)';
|
|
34
|
+
},
|
|
26
35
|
},
|
|
27
36
|
};
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const finish = data.candidates?.[0]?.finishReason;
|
|
42
|
-
for (const p of parts) if (p.text) onChunk(p.text);
|
|
43
|
-
const calls = parts.filter(p => p.functionCall);
|
|
44
|
-
if (finish === 'STOP' || calls.length === 0) break;
|
|
45
|
-
const toolResults = await Promise.all(calls.map(async p => {
|
|
46
|
-
const { name, args } = p.functionCall;
|
|
47
|
-
onTool(name, args);
|
|
48
|
-
let output;
|
|
49
|
-
try { output = String(await toolHandlers[name](args)); }
|
|
50
|
-
catch (e) { output = 'error: ' + e.message; }
|
|
51
|
-
return { functionResponse: { name, response: { output } } };
|
|
52
|
-
}));
|
|
53
|
-
contents = [...contents, { role: 'model', parts }, { role: 'user', parts: toolResults }];
|
|
38
|
+
export async function agentGenerate(apiKey, model, messages, onChunk, onTool) {
|
|
39
|
+
Object.assign(window.__debug = window.__debug || {}, {
|
|
40
|
+
agent: { model, active: true },
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
for await (const ev of streamGemini({ model, messages, tools: TOOLS, apiKey, maxOutputTokens: 8192 }).fullStream) {
|
|
44
|
+
if (ev.type === 'text-delta') onChunk(ev.textDelta);
|
|
45
|
+
else if (ev.type === 'tool-call') onTool(ev.toolName, ev.args);
|
|
46
|
+
else if (ev.type === 'error') throw ev.error;
|
|
47
|
+
}
|
|
48
|
+
} finally {
|
|
49
|
+
window.__debug.agent.active = false;
|
|
54
50
|
}
|
|
55
51
|
}
|
package/docs/app.js
CHANGED
|
@@ -14,19 +14,6 @@ async function fetchModels(apiKey) {
|
|
|
14
14
|
.map(m => ({ id: m.name.replace('models/', ''), label: m.displayName || m.name }));
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function convertMessages(messages) {
|
|
18
|
-
const out = [];
|
|
19
|
-
for (const m of messages) {
|
|
20
|
-
const role = m.role === 'assistant' ? 'model' : 'user';
|
|
21
|
-
if (typeof m.content === 'string') {
|
|
22
|
-
if (m.content) out.push({ role, parts: [{ text: m.content }] });
|
|
23
|
-
} else if (Array.isArray(m.content)) {
|
|
24
|
-
const parts = m.content.flatMap(b => b.type === 'text' && b.text ? [{ text: b.text }] : []);
|
|
25
|
-
if (parts.length) out.push({ role, parts });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return out;
|
|
29
|
-
}
|
|
30
17
|
|
|
31
18
|
class BirdChat extends HTMLElement {
|
|
32
19
|
constructor() {
|
|
@@ -132,7 +119,7 @@ class BirdChat extends HTMLElement {
|
|
|
132
119
|
wrap.appendChild(cursor);
|
|
133
120
|
const list = this.querySelector('#msg-list');
|
|
134
121
|
if (list) list.appendChild(wrap);
|
|
135
|
-
await agentGenerate(apiKey, model,
|
|
122
|
+
await agentGenerate(apiKey, model, messages,
|
|
136
123
|
chunk => { full += chunk; streamEl.textContent = full; const l = this.querySelector('#msg-list'); if (l) l.scrollTop = l.scrollHeight; },
|
|
137
124
|
(name, args) => { full += `\n[tool: ${name}(${JSON.stringify(args)})]\n`; streamEl.textContent = full; }
|
|
138
125
|
);
|