thebird 1.2.8 → 1.2.9

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.
Files changed (2) hide show
  1. package/docs/app.js +56 -23
  2. package/package.json +1 -1
package/docs/app.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import { createElement, applyDiff } from 'webjsx';
2
2
 
3
- const MODELS = [
4
- 'gemini-2.0-flash',
5
- 'gemini-2.0-flash-thinking-exp',
6
- 'gemini-1.5-pro',
7
- 'gemini-1.5-flash',
8
- ];
3
+ const MODELS_API = key => `https://generativelanguage.googleapis.com/v1beta/models?key=${key}`;
4
+
5
+ async function fetchModels(apiKey) {
6
+ const res = await fetch(MODELS_API(apiKey));
7
+ if (!res.ok) throw new Error(`Models API ${res.status}: ${await res.text()}`);
8
+ const { models = [] } = await res.json();
9
+ return models
10
+ .filter(m => m.supportedGenerationMethods?.includes('generateContent'))
11
+ .map(m => ({ id: m.name.replace('models/', ''), label: m.displayName || m.name }));
12
+ }
9
13
 
10
14
  function convertMessages(messages) {
11
15
  const contents = [];
@@ -45,20 +49,42 @@ class BirdChat extends HTMLElement {
45
49
  this.state = {
46
50
  messages: [],
47
51
  streaming: false,
48
- model: MODELS[0],
52
+ model: 'gemini-2.5-flash',
49
53
  apiKey: localStorage.getItem('gemini_api_key') || '',
54
+ models: [],
55
+ modelsLoading: false,
50
56
  status: '',
51
57
  streamingText: '',
52
58
  };
53
- window.__debug = { get state() { return this.state; }.bind(this), get messages() { return this.state.messages; }.bind(this) };
59
+ window.__debug = {
60
+ get state() { return this.state; }.bind(this),
61
+ get messages() { return this.state.messages; }.bind(this),
62
+ get models() { return this.state.models; }.bind(this),
63
+ };
54
64
  }
55
65
 
56
- connectedCallback() { this.render(); }
66
+ connectedCallback() {
67
+ this.render();
68
+ if (this.state.apiKey) this.loadModels(this.state.apiKey);
69
+ }
57
70
 
58
71
  setState(patch) { Object.assign(this.state, patch); this.render(); }
59
72
 
73
+ async loadModels(apiKey) {
74
+ this.setState({ modelsLoading: true, status: '' });
75
+ try {
76
+ const models = await fetchModels(apiKey);
77
+ const current = this.state.model;
78
+ const first = models[0]?.id || 'gemini-2.5-flash';
79
+ const model = models.find(m => m.id === current) ? current : first;
80
+ this.setState({ models, model, modelsLoading: false });
81
+ } catch (err) {
82
+ this.setState({ modelsLoading: false, status: 'Failed to load models: ' + (err?.message || String(err)) });
83
+ }
84
+ }
85
+
60
86
  render() {
61
- const { messages, streaming, model, apiKey, status, streamingText } = this.state;
87
+ const { messages, streaming, model, apiKey, models, modelsLoading, status, streamingText } = this.state;
62
88
  applyDiff(this, (
63
89
  <div class="flex flex-col h-full">
64
90
  <header class="navbar bg-base-200 border-b border-base-300 gap-2 flex-wrap px-4 py-2">
@@ -71,15 +97,26 @@ class BirdChat extends HTMLElement {
71
97
  class="input input-sm input-bordered flex-1 min-w-[160px]"
72
98
  placeholder="GEMINI_API_KEY"
73
99
  value={apiKey}
74
- onchange={e => { const v = e.target.value.trim(); localStorage.setItem('gemini_api_key', v); this.setState({ apiKey: v }); }}
100
+ onchange={e => {
101
+ const v = e.target.value.trim();
102
+ localStorage.setItem('gemini_api_key', v);
103
+ this.setState({ apiKey: v });
104
+ if (v) this.loadModels(v);
105
+ }}
75
106
  />
76
- <select
77
- class="select select-sm select-bordered"
78
- value={model}
79
- onchange={e => this.setState({ model: e.target.value })}
80
- >
81
- {MODELS.map(m => <option value={m} selected={m === model}>{m}</option>)}
82
- </select>
107
+ <div class="relative">
108
+ {modelsLoading
109
+ ? <span class="loading loading-spinner loading-sm text-primary"></span>
110
+ : <select
111
+ class="select select-sm select-bordered"
112
+ value={model}
113
+ disabled={models.length === 0}
114
+ onchange={e => this.setState({ model: e.target.value })}
115
+ >
116
+ {(models.length === 0 ? [{ id: model, label: model }] : models).map(m => <option value={m.id} selected={m.id === model}>{m.label}</option>)}
117
+ </select>
118
+ }
119
+ </div>
83
120
  <button class="btn btn-sm btn-ghost" onclick={() => this.setState({ messages: [], status: '' })}>Clear</button>
84
121
  </div>
85
122
  </header>
@@ -97,11 +134,7 @@ class BirdChat extends HTMLElement {
97
134
  <div class="msg-bubble card bg-base-200 text-base-content px-4 py-3 text-sm leading-relaxed">{streamingText}<span class="animate-pulse ml-1">▋</span></div>
98
135
  </div>
99
136
  )}
100
- {!streamingText && streaming && (
101
- <div class="flex justify-start">
102
- <div class="card bg-base-200 px-4 py-3"><span class="loading loading-dots loading-sm"></span></div>
103
- </div>
104
- )}
137
+ {!streamingText && streaming && <div class="flex justify-start"><div class="card bg-base-200 px-4 py-3"><span class="loading loading-dots loading-sm"></span></div></div>}
105
138
  </div>
106
139
 
107
140
  {status && <div class="text-xs text-error px-4 pb-1">{status}</div>}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thebird",
3
- "version": "1.2.8",
3
+ "version": "1.2.9",
4
4
  "description": "Anthropic SDK to Gemini streaming bridge — drop-in proxy that translates Anthropic message format and tool calls to Google Gemini",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",