feat: fetch APInex models from main thread, populate default-model dropdown (networkAccess added)
This commit is contained in:
1 parent
1468570d0f
commit
ce5aa025aa
3 files changed
+64
-2
No files matched your search
+25
@@ -502,6 +502,31 @@ export default function () {
|
||||
})
|
||||
})
|
||||
|
||||
// UI asks to list models for a provider. Network must run here (main thread) —
|
||||
// the plugin UI iframe is CSP-sandboxed and cannot fetch external URLs.
|
||||
on('FETCH_MODELS', async function (payload: { baseUrl: string, apiKey: string }) {
|
||||
try {
|
||||
const base = payload.baseUrl.replace(/\/+$/, '')
|
||||
const res = await fetch(base + '/models', {
|
||||
headers: { Authorization: 'Bearer ' + payload.apiKey }
|
||||
})
|
||||
if (!res.ok) {
|
||||
emit('MODELS_RESULT', { ok: false, error: 'HTTP ' + res.status, models: [] })
|
||||
return
|
||||
}
|
||||
const json: any = await res.json()
|
||||
let models: string[] = []
|
||||
if (Array.isArray(json.data)) {
|
||||
models = json.data.map((m: any) => m.id).filter((x: any) => typeof x === 'string' && x.length > 0)
|
||||
} else if (Array.isArray(json.models)) {
|
||||
models = json.models.map((m: any) => m.id || m).filter((x: any) => typeof x === 'string')
|
||||
}
|
||||
emit('MODELS_RESULT', { ok: true, models })
|
||||
} catch (e) {
|
||||
emit('MODELS_RESULT', { ok: false, error: String(e), models: [] })
|
||||
}
|
||||
})
|
||||
|
||||
showUI({ width: 320, height: 200, themeColors: true, title: 'AI assistant (MVP)' })
|
||||
|
||||
// Restore persisted window placement (saved in clientStorage on the Figma file).
|
||||
|
||||
+3
-1
@@ -12,6 +12,7 @@ export interface ProviderConfig {
|
||||
baseUrl: string
|
||||
apiKey: string
|
||||
defaultModel: string
|
||||
models: string[]
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
@@ -44,7 +45,8 @@ export function createApinexProvider (): ProviderConfig {
|
||||
type: 'apinex',
|
||||
baseUrl: APINEX_BASE_URL,
|
||||
apiKey: '',
|
||||
defaultModel: ''
|
||||
defaultModel: '',
|
||||
models: []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-1
@@ -60,6 +60,30 @@ function SettingsPanel (props: { settings: Settings, setSettings: (s: Settings)
|
||||
const [showKey, setShowKey] = useState(false)
|
||||
const [savedNote, setSavedNote] = useState(false)
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const [modelsError, setModelsError] = useState<string | null>(null)
|
||||
const settingsRef = useRef<Settings>(settings)
|
||||
settingsRef.current = settings
|
||||
|
||||
useEffect(function () {
|
||||
const dispose = on('MODELS_RESULT', function (payload: { ok: boolean, models: string[], error?: string }) {
|
||||
if (!payload.ok) { setModelsError(payload.error != null ? payload.error : 'Failed to load models'); return }
|
||||
setModelsError(null)
|
||||
const prev = settingsRef.current
|
||||
setSettings({
|
||||
...prev,
|
||||
providers: prev.providers.map(function (p) {
|
||||
return p.id === prev.activeProviderId ? { ...p, models: payload.models } : p
|
||||
})
|
||||
})
|
||||
})
|
||||
return dispose
|
||||
}, [])
|
||||
|
||||
function fetchModels () {
|
||||
if (active == null) return
|
||||
setModelsError(null)
|
||||
emit('FETCH_MODELS', { baseUrl: active.baseUrl, apiKey: active.apiKey })
|
||||
}
|
||||
|
||||
const active = settings.providers.find(function (p) { return p.id === settings.activeProviderId }) || null
|
||||
|
||||
@@ -159,7 +183,18 @@ function SettingsPanel (props: { settings: Settings, setSettings: (s: Settings)
|
||||
</label>
|
||||
<label style={fieldStyle}>
|
||||
<span style={labelStyle}>Default model</span>
|
||||
<Textbox value={active.defaultModel} placeholder="gpt-5-6-terra" onValueInput={function (v: string) { patchActive({ defaultModel: v }) }} />
|
||||
<div style={{ display: 'flex', gap: 'var(--space-4)', alignItems: 'center' }}>
|
||||
<Dropdown
|
||||
placeholder={active.models.length > 0 ? 'Select model' : 'Fetch models first'}
|
||||
value={active.defaultModel}
|
||||
onValueChange={function (id: string) { patchActive({ defaultModel: id }) }}
|
||||
options={(active.models || []).map(function (m) { return { value: m, text: m } })}
|
||||
/>
|
||||
<Button onClick={fetchModels} disabled={active.apiKey.trim() === ''}>Fetch</Button>
|
||||
</div>
|
||||
{modelsError != null && (
|
||||
<Muted style={{ color: 'var(--figma-color-text-danger)' }}>{modelsError}</Muted>
|
||||
)}
|
||||
</label>
|
||||
</section>
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user