@byte-42/fantastico-sdk 0.0.0-stage → 0.21.0
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/LICENSE +21 -0
- package/README.md +470 -3
- package/dist/activity.d.ts +72 -0
- package/dist/activity.js +22 -0
- package/dist/auth.d.ts +216 -0
- package/dist/auth.js +224 -0
- package/dist/backoff.d.ts +48 -0
- package/dist/backoff.js +31 -0
- package/dist/browser-actions.d.ts +28 -0
- package/dist/browser-actions.js +25 -0
- package/dist/client.d.ts +410 -0
- package/dist/client.js +410 -0
- package/dist/decisions.d.ts +149 -0
- package/dist/decisions.js +71 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +7 -0
- package/dist/llm.d.ts +40 -0
- package/dist/llm.js +74 -0
- package/dist/local-tools.d.ts +72 -0
- package/dist/local-tools.js +55 -0
- package/dist/max-tokens.d.ts +27 -0
- package/dist/max-tokens.js +16 -0
- package/dist/mcp.d.ts +14 -0
- package/dist/mcp.js +55 -0
- package/dist/orchestrator.d.ts +105 -0
- package/dist/orchestrator.js +296 -0
- package/dist/provider-error.d.ts +165 -0
- package/dist/provider-error.js +156 -0
- package/dist/raw.d.ts +48 -0
- package/dist/raw.js +72 -0
- package/dist/schema-digest.d.ts +25 -0
- package/dist/schema-digest.js +20 -0
- package/dist/speech.d.ts +107 -0
- package/dist/speech.js +48 -0
- package/dist/surface.d.ts +35 -0
- package/dist/surface.js +13 -0
- package/dist/tool-definition.d.ts +22 -0
- package/dist/tool-definition.js +10 -0
- package/dist/tool-origin.d.ts +32 -0
- package/dist/tool-origin.js +65 -0
- package/dist/types.d.ts +734 -0
- package/dist/types.js +27 -0
- package/package.json +48 -6
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import type { FantasticoOptions, ChatCompletionCreateParams, ChatCompletionResult, ToolDefinition, RawCompletionCreateParams, RawChatCompletion, AccountCreateParams, AccountUpdateParams, AccountInfo, AccountKeyInfo, IssuedAccountKey, RuntimeKeyInfo, RuntimeKeyHandle, ContoBalance, UsageRecordParams, UsageRecordResult, ResumeDecision, OpenRouterModel } from "./types.js";
|
|
2
|
+
import { type Surface } from "./surface.js";
|
|
3
|
+
import type { ChatActivityOptions } from "./activity.js";
|
|
4
|
+
import { type RegisteredTool, type LocalToolDefinition } from "./local-tools.js";
|
|
5
|
+
import { type EvaluateDecisionParams, type DecisionResult, type DecisionToolConfig } from "./decisions.js";
|
|
6
|
+
import { type SpeechCreateParams, type SpeechResult, type SpeechModel } from "./speech.js";
|
|
7
|
+
export declare class Fantastico {
|
|
8
|
+
private readonly opts;
|
|
9
|
+
private readonly auth;
|
|
10
|
+
private readonly fetchImpl;
|
|
11
|
+
private readonly mcpExecutor;
|
|
12
|
+
private readonly localTools;
|
|
13
|
+
/** Costo non ancora regolato per tenant: si salda alla chiamata successiva (delta). */
|
|
14
|
+
private readonly unsettled;
|
|
15
|
+
/** Nome visibile dell'assistente, se impostato — un solo posto da cui prompt e UI
|
|
16
|
+
* lo leggono, invece di ripetere la stringa (vedi FantasticoOptions.name). */
|
|
17
|
+
readonly name?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Su quale delle due superfici parla questo client, dedotto dal prefisso della credenziale
|
|
20
|
+
* (`surface.ts`). Leggibile perché chi integra con l'una o con l'altra ha a disposizione cose
|
|
21
|
+
* diverse — `accounts` di qua, `balance` di là — e un `if` sulla stringa della chiave, scritto
|
|
22
|
+
* a valle, sarebbe la seconda copia della stessa deduzione.
|
|
23
|
+
*/
|
|
24
|
+
readonly surface: Surface;
|
|
25
|
+
readonly chat: {
|
|
26
|
+
completions: {
|
|
27
|
+
/** `options.onEvent` riceve gli eventi del turno mentre accadono (`activity.ts`). */
|
|
28
|
+
create: (params: ChatCompletionCreateParams, options?: ChatActivityOptions) => Promise<ChatCompletionResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Riprende un'orchestrazione fermata con `status: "pending"` (conferma umana o esito
|
|
31
|
+
* di un'azione client). `state` è quello ricevuto in `PendingCompletion.state`, passato
|
|
32
|
+
* identico — non va ispezionato né modificato.
|
|
33
|
+
*/
|
|
34
|
+
resume: (state: unknown, decisions: ResumeDecision[], context?: unknown, options?: ChatActivityOptions) => Promise<ChatCompletionResult>;
|
|
35
|
+
/** Nessuna orchestrazione: una chiamata, tool del chiamante, il loop resta a lui. */
|
|
36
|
+
createRaw: (params: RawCompletionCreateParams) => Promise<RawChatCompletion>;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Tool del marketplace FANTASTICO, per chi gestisce il proprio loop via createRaw().
|
|
41
|
+
*
|
|
42
|
+
* **`user` nomina il conto**, come in `create()`: i tool token arrivano col conio e sono la
|
|
43
|
+
* credenziale con cui quel tool verrà pagato, quindi chiedere l'elenco per un tenant significa
|
|
44
|
+
* coniare — e far pagare — sul conto giusto. Omesso, è il conto del proprietario.
|
|
45
|
+
*/
|
|
46
|
+
readonly tools: {
|
|
47
|
+
list: (user?: string) => Promise<ToolDefinition[]>;
|
|
48
|
+
execute: (name: string, args: Record<string, unknown>, user?: string) => Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* **Cosa il progetto ha abilitato**: id e nome, senza coniare. Dalla 0.14.0.
|
|
51
|
+
*
|
|
52
|
+
* È la domanda di chi **configura**, non di chi esegue: un pannello che deve offrire le
|
|
53
|
+
* skill disponibili, un controllo di installazione. Per questo non porta endpoint, token né
|
|
54
|
+
* digest — quelli arrivano col conio, uno per tool, ed è lì che restano.
|
|
55
|
+
*
|
|
56
|
+
* La differenza con `list()` è **quanto costa chiederlo**: `list()` conia (e quindi impegna
|
|
57
|
+
* un blocco sul conto) perché deve restituire i tool *pronti all'uso*, verificati contro il
|
|
58
|
+
* documento che serviranno. Questa no: non impegna niente e non apre nessuna chiave, quindi
|
|
59
|
+
* è quella giusta ogni volta che la risposta serve a mostrare un elenco e non a chiamarlo.
|
|
60
|
+
*/
|
|
61
|
+
enabled: () => Promise<Array<{
|
|
62
|
+
id: string;
|
|
63
|
+
name: string;
|
|
64
|
+
}>>;
|
|
65
|
+
};
|
|
66
|
+
/** Catalogo modelli disponibili sul provider (OpenRouter), per chi vuole scegliere
|
|
67
|
+
* `model` senza conoscerlo a priori — es. un selettore in UI. Risposta cachata dal
|
|
68
|
+
* Cloud per qualche minuto: chiamarlo spesso non genera traffico verso il provider. */
|
|
69
|
+
readonly models: {
|
|
70
|
+
list: () => Promise<OpenRouterModel[]>;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Anagrafica dei conti del progetto — i tenant finali a cui è attribuito il consumo.
|
|
74
|
+
*
|
|
75
|
+
* Progetto e ambiente **non si passano**: sono quelli della project key con cui è stato
|
|
76
|
+
* costruito il client. Non esiste un parametro con cui operare sul progetto di qualcun
|
|
77
|
+
* altro, e quindi non c'è la classe di errore corrispondente.
|
|
78
|
+
*/
|
|
79
|
+
readonly accounts: {
|
|
80
|
+
create: (params: AccountCreateParams) => Promise<AccountInfo>;
|
|
81
|
+
list: () => Promise<AccountInfo[]>;
|
|
82
|
+
get: (userKey: string) => Promise<AccountInfo>;
|
|
83
|
+
update: (userKey: string, params: AccountUpdateParams) => Promise<AccountInfo>;
|
|
84
|
+
/**
|
|
85
|
+
* Chiude la prova di un conto aperto con `type: "trial"`: da qui è un conto a pagamento.
|
|
86
|
+
* Dalla 0.13.0.
|
|
87
|
+
*
|
|
88
|
+
* **La chiama il verticale che ha incassato l'abbonamento**, perché è l'unico a sapere che è
|
|
89
|
+
* stato pagato. Senza, il conto resta una prova e il Cloud lo sospende alla scadenza anche se
|
|
90
|
+
* il cliente nel frattempo si è abbonato.
|
|
91
|
+
*
|
|
92
|
+
* Cosa cambia per quel conto: la parte non spesa della concessione di prova torna alla riserva
|
|
93
|
+
* del progetto (era un prestito per provare), cominciano la quota periodica del piano e la fee
|
|
94
|
+
* per conto, e se era sospeso **dalla propria scadenza** torna attivo — uno sospeso da un
|
|
95
|
+
* operatore resta sospeso, di proposito.
|
|
96
|
+
*
|
|
97
|
+
* Una volta sola e senza ritorno: su un conto che non è una prova risponde 422.
|
|
98
|
+
*/
|
|
99
|
+
upgrade: (userKey: string) => Promise<AccountInfo>;
|
|
100
|
+
/**
|
|
101
|
+
* La **chiave di conto**: si emette quando l'operatività di quel conto va delegata a un
|
|
102
|
+
* perimetro che non controlli — il plugin di un cliente, un dispositivo, un'installazione.
|
|
103
|
+
* Chi la riceve può coniare runtime key **solo** entro il credito di
|
|
104
|
+
* quel conto, e non può nominarne un altro.
|
|
105
|
+
*
|
|
106
|
+
* È opzionale: un conto senza chiave resta operato da questo stesso client, con la
|
|
107
|
+
* project key, esattamente come oggi. Emetterla è un atto in più e volontario.
|
|
108
|
+
*
|
|
109
|
+
* **Una sola chiave viva per conto**: se ne esiste già una, `issue` risponde 409 —
|
|
110
|
+
* per sostituirla si usa `rotate`, che invalida la precedente nello stesso istante.
|
|
111
|
+
*/
|
|
112
|
+
keys: {
|
|
113
|
+
/** Le chiavi attive del progetto+ambiente: quali conti hanno una credenziale in giro. */
|
|
114
|
+
list: () => Promise<AccountKeyInfo[]>;
|
|
115
|
+
/** Emette la chiave. **L'unico momento in cui il valore in chiaro esiste.** */
|
|
116
|
+
issue: (userKey: string, label?: string) => Promise<IssuedAccountKey>;
|
|
117
|
+
/** Ne emette una nuova e invalida la precedente. Da usare dopo un sospetto, o su un clone. */
|
|
118
|
+
rotate: (userKey: string) => Promise<IssuedAccountKey>;
|
|
119
|
+
/** Revoca senza sostituire: il conto resta, e torna operabile da qui. */
|
|
120
|
+
revoke: (userKey: string) => Promise<{
|
|
121
|
+
revoked: number;
|
|
122
|
+
}>;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Le runtime key OpenRouter che questo progetto ha ancora aperte.
|
|
127
|
+
*
|
|
128
|
+
* **Esiste perché il segreto non si conserva.** La credenziale vive solo nella memoria di chi
|
|
129
|
+
* l'ha coniata: un processo riavviato, o una seconda istanza dello stesso client, non sa
|
|
130
|
+
* più nominare le chiavi che ha in giro — mentre ognuna di esse occupa uno slot del piano
|
|
131
|
+
* (`maxOpenKeys`) finché non liquida. Questa lettura le rende nominabili, e quindi
|
|
132
|
+
* restituibili, invece di lasciarle scadere.
|
|
133
|
+
*
|
|
134
|
+
* Senza `userKey` è il conto del proprietario, lo stesso che il conio userebbe.
|
|
135
|
+
*/
|
|
136
|
+
readonly runtimeKeys: {
|
|
137
|
+
/**
|
|
138
|
+
* La chiave con cui questo client sta lavorando, **coniandone una se non ne ha**.
|
|
139
|
+
*
|
|
140
|
+
* È la sostituzione onesta del vecchio "conia tu e passami la key": chi vuole vedere handle,
|
|
141
|
+
* tetto, impegno e scadenza all'avvio — o vuole poter **restituire** la chiave all'uscita,
|
|
142
|
+
* invece di lasciare uno slot di `maxOpenKeys` occupato per il TTL residuo — la chiama qui e
|
|
143
|
+
* continua a usare il client normalmente. Il segreto resta dentro, dove può scadere e
|
|
144
|
+
* rinnovarsi senza che nessuno se ne accorga.
|
|
145
|
+
*
|
|
146
|
+
* Chiamarla non è obbligatorio: la prima `create()` conia da sé.
|
|
147
|
+
*
|
|
148
|
+
* **`user` nomina il conto che paga**, ed è lo stesso campo di `create()`: una chiave è di un
|
|
149
|
+
* conto, quindi chiedere "la chiave di questo client" senza dire quale conto significa quella
|
|
150
|
+
* del proprietario del progetto. Chi chiama per conto di un tenant — per leggere i tool che il
|
|
151
|
+
* conio gli concede, per esempio — deve nominarlo, o aprirà un hold sul conto sbagliato.
|
|
152
|
+
*/
|
|
153
|
+
ensure: (user?: string) => Promise<RuntimeKeyHandle>;
|
|
154
|
+
/**
|
|
155
|
+
* La chiave in mano **adesso**, senza coniarne una: `null` se non se n'è ancora coniata
|
|
156
|
+
* nessuna, o se l'ultima è stata scartata dopo un errore del provider.
|
|
157
|
+
*
|
|
158
|
+
* È la metà in sola lettura di `ensure`, e serve quando restituire una chiave è l'ultima cosa
|
|
159
|
+
* che si fa: fra l'avvio e l'uscita l'SDK può averne coniata un'altra — una chiave si esaurisce
|
|
160
|
+
* o scade a metà lavoro — e restituire l'handle di partenza lascerebbe viva quella vera,
|
|
161
|
+
* occupando uno slot di `maxOpenKeys` per il TTL residuo. Con `ensure` non si potrebbe: a
|
|
162
|
+
* processo in chiusura conierebbe la chiave che si sta cercando di rendere.
|
|
163
|
+
*
|
|
164
|
+
* `user` come in `ensure`: la chiave in mano **di quel conto**.
|
|
165
|
+
*/
|
|
166
|
+
current: (user?: string) => RuntimeKeyHandle | null;
|
|
167
|
+
list: (userKey?: string) => Promise<RuntimeKeyInfo[]>;
|
|
168
|
+
/**
|
|
169
|
+
* Restituisce una chiave prima della scadenza: smette di funzionare sul provider subito, e
|
|
170
|
+
* il suo slot torna libero appena lo scheduler la liquida — un paio di minuti invece del
|
|
171
|
+
* TTL residuo. `revoked: false` se era già stata revocata.
|
|
172
|
+
*
|
|
173
|
+
* **L'handle la nomina, il conto la autorizza**: come per il conio, senza `userKey` è il
|
|
174
|
+
* conto del proprietario. Una chiave di un altro conto viene rifiutata con le stesse parole
|
|
175
|
+
* di una che non esiste.
|
|
176
|
+
*/
|
|
177
|
+
revoke: (recordId: string, userKey?: string) => Promise<{
|
|
178
|
+
revoked: boolean;
|
|
179
|
+
}>;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Modelli decisionali "System One" (Jev di TypeSafe e affini) — non chat, non `/chat/completions`:
|
|
183
|
+
* uno stato in ingresso, un verdetto tipizzato con probabilità in uscita, sullo stesso conto e
|
|
184
|
+
* la stessa runtime key di ogni altra chiamata (`decisions.ts`).
|
|
185
|
+
*
|
|
186
|
+
* **`evaluate` è la chiamata diretta**, con la stessa contabilità di `createRaw()` — prenota il
|
|
187
|
+
* budget del tenant, conia/ri-conia se il provider rifiuta, regola il costo reale al giro
|
|
188
|
+
* successivo. **`tool` è l'integrazione nel loop di tool-use**: costruisce un
|
|
189
|
+
* `LocalToolDefinition` che un LLM conversazionale (avviato con `chat.completions.create()`)
|
|
190
|
+
* può chiamare a metà conversazione per ottenere una decisione veloce senza uscire dal loop —
|
|
191
|
+
* es. instradare un ticket, o segnalare un contenuto, mentre il modello principale continua a
|
|
192
|
+
* scrivere la risposta. Registralo con `ai.addTool(ai.decisions.tool({...}))`.
|
|
193
|
+
*/
|
|
194
|
+
readonly decisions: {
|
|
195
|
+
evaluate: (params: EvaluateDecisionParams) => Promise<DecisionResult>;
|
|
196
|
+
tool: <TContext = unknown>(config: DecisionToolConfig<TContext>) => LocalToolDefinition<TContext>;
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Voce sintetica (text-to-speech), drop-in di `audio.speech.create()` dell'SDK OpenAI — sullo
|
|
200
|
+
* stesso conto e la stessa runtime key di ogni altra chiamata (`speech.ts`). Dalla 0.20.0.
|
|
201
|
+
*
|
|
202
|
+
* **`create`** ha la stessa contabilità di `createRaw()`: prenota il budget del tenant, conia o
|
|
203
|
+
* ri-conia se il provider rifiuta la chiave, e **non dichiara nessun costo** — la spesa entra
|
|
204
|
+
* nell'`usage` della runtime key presso il provider come quella di una chat, e il ledger la
|
|
205
|
+
* liquida sull'hold del conio. Verificato sul campo il 2026-10-17.
|
|
206
|
+
*
|
|
207
|
+
* **`models`** è il catalogo dei modelli TTS con le voci che ciascuno ammette
|
|
208
|
+
* (`supported_voices`) e il listino, per un selettore che faccia scegliere la voce anche in
|
|
209
|
+
* base al costo come già fa per i modelli di chat. È un catalogo a sé: `ai.models.list()`
|
|
210
|
+
* **non li contiene** — OpenRouter li espone solo filtrando per modalità in uscita.
|
|
211
|
+
*/
|
|
212
|
+
readonly audio: {
|
|
213
|
+
speech: {
|
|
214
|
+
create: (params: SpeechCreateParams) => Promise<SpeechResult>;
|
|
215
|
+
models: () => Promise<SpeechModel[]>;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Il saldo del **proprio** conto — solo con una chiave di conto (`/api/edge/balance`).
|
|
220
|
+
*
|
|
221
|
+
* Con una project key non esiste, e non è una lacuna da colmare: lì i conti sono molti e si
|
|
222
|
+
* nominano, quindi la stessa domanda è `ai.accounts.get(userKey)`. Le due superfici non offrono
|
|
223
|
+
* le stesse cose perché non hanno la stessa identità, e fingere il contrario significherebbe
|
|
224
|
+
* rispondere con il saldo di qualcun altro.
|
|
225
|
+
*/
|
|
226
|
+
readonly balance: () => Promise<ContoBalance>;
|
|
227
|
+
/**
|
|
228
|
+
* @deprecated Non addebita niente e non manda nessuna richiesta: risolve con `amount: 0`.
|
|
229
|
+
* Tenuto perché un client scritto contro una versione precedente continui a compilare **e a
|
|
230
|
+
* girare**.
|
|
231
|
+
*
|
|
232
|
+
* Dichiarava il consumo di un servizio marketplace a esecuzione locale (`POST /api/usage`), ed
|
|
233
|
+
* era l'unico addebito del marketplace deciso da **chi chiama** invece che dal tool. Non c'è
|
|
234
|
+
* più perché quel modello non c'è più: ogni tool si registra a un endpoint, ci viene chiamato
|
|
235
|
+
* con un tool token e dichiara da sé quanto è costata la chiamata, contro un tetto che il conio
|
|
236
|
+
* gli ha dato. Prezzare a unità non è morto — è aritmetica che il
|
|
237
|
+
* tool fa; è morto il *client* che dichiara le unità.
|
|
238
|
+
*
|
|
239
|
+
* **Un servizio che si faceva pagare così, oggi, non si fa pagare.** Questo ramo tiene in piedi
|
|
240
|
+
* il codice, non il ricavo: se questa chiamata era l'unica contabilità di un servizio, quel
|
|
241
|
+
* servizio va registrato nel marketplace e deve fatturarsi contro il proprio tool token.
|
|
242
|
+
*/
|
|
243
|
+
readonly usage: {
|
|
244
|
+
record: (params: UsageRecordParams) => Promise<UsageRecordResult>;
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* La sessione di attività in corso, o `null` prima del primo conio.
|
|
248
|
+
*
|
|
249
|
+
* **Non è una credenziale**: non autorizza niente, si può stampare, loggare e mettere in un
|
|
250
|
+
* ticket — ed è esattamente ciò per cui esiste, visto che la query di supporto a valle è
|
|
251
|
+
* `WHERE activity_session_id = '…'`. Prima non usciva da `AuthClient`, quindi chi integrava non
|
|
252
|
+
* aveva modo di scrivere nei propri log l'unico id con cui quella spesa si ritrova.
|
|
253
|
+
*
|
|
254
|
+
* Può **cambiare da sola**: il ledger ne sostituisce in silenzio una che non riconosce più
|
|
255
|
+
* (potata, o mai stata di questo conto), quindi va riletta dopo un conio e non memorizzata una
|
|
256
|
+
* volta per tutte.
|
|
257
|
+
*/
|
|
258
|
+
get activitySessionId(): string | null;
|
|
259
|
+
constructor(options: FantasticoOptions);
|
|
260
|
+
/**
|
|
261
|
+
* Il tenant a cui attribuire questa chiamata — **e il rifiuto di nominarne uno sull'edge**.
|
|
262
|
+
*
|
|
263
|
+
* `user` è il campo OpenAI-compat con cui una project key dice a quale conto del progetto va il
|
|
264
|
+
* consumo. Con una chiave di conto quel conto è già deciso: è quello della credenziale, e
|
|
265
|
+
* nessuna rotta edge ne legge uno dal corpo. Ignorarlo in silenzio sarebbe peggio che
|
|
266
|
+
* rifiutarlo — chi lo passa crede di star separando due tenant, e vedrebbe invece la spesa di
|
|
267
|
+
* tutti e due finire sullo stesso conto, senza niente che lo dica.
|
|
268
|
+
*/
|
|
269
|
+
private tenantOf;
|
|
270
|
+
/**
|
|
271
|
+
* L'anagrafica dei conti, e il posto unico in cui la sua superficie si verifica.
|
|
272
|
+
*
|
|
273
|
+
* `/api/conti/*` autentica con la project key e descrive **tutti** i conti di un progetto: non
|
|
274
|
+
* esiste su edge, e non è una rotta che manca — una chiave di conto ne vale uno solo, quindi
|
|
275
|
+
* "elenca i conti" lì è una domanda che non ha un soggetto.
|
|
276
|
+
*/
|
|
277
|
+
private conti;
|
|
278
|
+
/** Registra un tool locale o un'azione-client dopo la costruzione (in alternativa/oltre a
|
|
279
|
+
* passarli nel costruttore via `tools`). */
|
|
280
|
+
addTool(tool: RegisteredTool): void;
|
|
281
|
+
/**
|
|
282
|
+
* Conia la runtime key e ci esegue `run`. Se il provider risponde `401`/`402` — o, meglio, se
|
|
283
|
+
* classifica lui stesso l'errore come `authentication`/`payment_required` — NON è un verdetto
|
|
284
|
+
* affidabile che "sono finiti i fondi del cliente": può essere il tetto di QUESTA key esaurito
|
|
285
|
+
* prima della sua scadenza per tempo, oppure un problema del tutto estraneo al cliente
|
|
286
|
+
* (una key già ritirata, o un problema del provider). L'ambiguità è **documentata dal
|
|
287
|
+
* provider**: il suo 402 vale "your account **or API key** has insufficient credits", e la doc
|
|
288
|
+
* dice apertamente di non distinguere i due casi. Quindi il verdetto non si dà, si prova: un solo
|
|
289
|
+
* retry a chiave nuova; se sparisce da solo, il chiamante non vede nulla. Se persiste, l'unico
|
|
290
|
+
* messaggio onesto è un errore di comunicazione col provider — MAI "sono finiti i tuoi fondi",
|
|
291
|
+
* perché a questo punto non lo sappiamo.
|
|
292
|
+
*
|
|
293
|
+
* **Su cosa si decide.** Su `ProviderError` (`provider-error.ts`), cioè sullo stato HTTP e su
|
|
294
|
+
* `metadata.error_type`, mai sul testo del messaggio. La regex che c'era prima leggeva la prosa
|
|
295
|
+
* del provider perché lo stato non sopravviveva alla pila: quel file racconta i due modi in cui
|
|
296
|
+
* sbagliava. Conseguenza per chi inietta un `LlmClient` proprio: per partecipare al ri-conio deve
|
|
297
|
+
* lanciare un `ProviderError` (esportato dall'indice); un `Error` qualunque è un errore e basta.
|
|
298
|
+
*
|
|
299
|
+
* L'UNICO caso in cui "fondi insufficienti" è un'affermazione vera è quando è
|
|
300
|
+
* FANTASTICO STESSO a rifiutarsi di coniare (`FantasticoError` da `getRuntimeKey()`,
|
|
301
|
+
* es. `INSUFFICIENT_RESERVE`/`ALLOWANCE_EXHAUSTED`): lì il verdetto è nostro, autorevole,
|
|
302
|
+
* e si propaga così com'è — non va mai reinterpretato.
|
|
303
|
+
*/
|
|
304
|
+
/**
|
|
305
|
+
* Il `max_tokens` da mandare quando il chiamante non ne ha scritto uno.
|
|
306
|
+
*
|
|
307
|
+
* **Va calcolato dopo il conio e dentro il retry**, non una volta per chiamata: la chiave del
|
|
308
|
+
* secondo tentativo e un'altra chiave, con un tetto suo, e riusare il numero della prima e
|
|
309
|
+
* esattamente il caso che il retry esiste per riparare.
|
|
310
|
+
*
|
|
311
|
+
* `undefined` quando non c'e niente da calcolare — catalogo muto, o chiave capiente. In
|
|
312
|
+
* entrambi i casi non si scrive nulla sul filo. Vedi `max-tokens.ts` per il perche del conto.
|
|
313
|
+
*/
|
|
314
|
+
private defaultMaxTokens;
|
|
315
|
+
/**
|
|
316
|
+
* Come un errore del provider arriva al chiamante.
|
|
317
|
+
*
|
|
318
|
+
* Se il provider l'ha classificato, si passa il suo stato e un codice `PROVIDER_*` che dice **chi
|
|
319
|
+
* ha parlato**: `status` in questo SDK porta già, verbatim, lo stato HTTP del Cloud (`auth.ts`,
|
|
320
|
+
* `api()`), quindi il numero da solo non basta più a distinguere i due mittenti — e il `403` lo
|
|
321
|
+
* usano entrambi, per cose con rimedi opposti. Vedi `providerFailure()`.
|
|
322
|
+
*
|
|
323
|
+
* Quel che resta `INTERNAL_ERROR` 500 è solo ciò che davvero non sappiamo attribuire.
|
|
324
|
+
*/
|
|
325
|
+
private asFantasticoError;
|
|
326
|
+
/** La configurazione dei ritentativi transitori, con la scadenza entro cui è lecito dormire. */
|
|
327
|
+
private retryConfig;
|
|
328
|
+
/**
|
|
329
|
+
* Un `LlmClient` che si ri-conia da solo, **per singola chiamata**.
|
|
330
|
+
*
|
|
331
|
+
* È qui che sta la differenza con la versione precedente, e non è una rifinitura. Prima il
|
|
332
|
+
* ri-conio avvolgeva l'intera `orchestrate()`, e l'orchestratore riparte da `[...messages]`: un
|
|
333
|
+
* 402 al quarto turno non riprendeva il lavoro, lo **rigiocava da capo** — i turni 1-3 rimandati
|
|
334
|
+
* al modello e ripagati sulla chiave nuova, e i loro tool **rieseguiti**, quelli remoti con un
|
|
335
|
+
* altro addebito e quelli locali con i loro effetti. Il primo tentativo non veniva rimborsato:
|
|
336
|
+
* era semplicemente perso. E il momento in cui un tetto di chiave si esaurisce è, per
|
|
337
|
+
* costruzione, **a metà conversazione**: il ri-conio scattava quindi proprio dove rigiocare
|
|
338
|
+
* costava di più.
|
|
339
|
+
*
|
|
340
|
+
* Spostato qui, l'unica cosa rifatta è la richiesta HTTP che è fallita. Lo stato accumulato resta
|
|
341
|
+
* dov'è sempre stato, dentro l'orchestratore, che non si accorge di niente.
|
|
342
|
+
*
|
|
343
|
+
* **E il `max_tokens` si ricalcola da sé**, che è l'invariante che il vecchio commento
|
|
344
|
+
* raccomandava a voce: si calcola dentro `run`, cioè dopo il conio, quindi la chiave nuova porta
|
|
345
|
+
* il proprio tetto invece di ereditare il numero di quella scartata — che è esattamente il caso
|
|
346
|
+
* che il ri-conio esiste per riparare.
|
|
347
|
+
*/
|
|
348
|
+
private remintingLlm;
|
|
349
|
+
private withProviderRetry;
|
|
350
|
+
private createCompletion;
|
|
351
|
+
/**
|
|
352
|
+
* I tool remoti di questo giro: conia se serve, poi chiede a ciascun tool le proprie funzioni.
|
|
353
|
+
*
|
|
354
|
+
* **Il conio viene prima, ed e' l'unico ordine possibile** da quando la discovery non ha un
|
|
355
|
+
* ripiego: le concessioni — endpoint, token, digest — arrivano *con* la
|
|
356
|
+
* runtime key, quindi senza chiave non c'e' niente da scoprire. Prima l'ordine era comodo e non
|
|
357
|
+
* necessario, perche' senza token si ricadeva sul catalogo aggregato del Cloud; ora chiedere
|
|
358
|
+
* prima di coniare significa un elenco vuoto.
|
|
359
|
+
*
|
|
360
|
+
* **Qui e non in tre posti.** Lo facevano `create`, `resume` e `ai.tools`, e il terzo non coniava:
|
|
361
|
+
* `ai.tools.list()` su un client appena costruito rispondeva quindi sempre `[]` — la superficie
|
|
362
|
+
* che esiste apposta per chi orchestra da se, muta senza dire perche'.
|
|
363
|
+
*/
|
|
364
|
+
private remoteTools;
|
|
365
|
+
/**
|
|
366
|
+
* Riprende un'orchestrazione sospesa da create() (`status: "pending"`). Rifà lo stesso
|
|
367
|
+
* bootstrap di create() (MCP abilitati, runtime key) perché in un backend a richieste può
|
|
368
|
+
* arrivare come chiamata HTTP separata, anche minuti dopo — mai supporre che sia lo stesso
|
|
369
|
+
* processo che l'ha messa in pausa.
|
|
370
|
+
*/
|
|
371
|
+
private resumeCompletion;
|
|
372
|
+
/**
|
|
373
|
+
* Una sola chiamata a OpenRouter — nessuna orchestrazione interna, nessun tool eseguito
|
|
374
|
+
* da FANTASTICO. Riusa la stessa runtime key + prenotazione budget di create(): il
|
|
375
|
+
* chiamante ottiene lo stesso pattern "prenota-e-salda ad ogni giro"
|
|
376
|
+
* semplicemente richiamando createRaw() a ogni passo del proprio loop di tool-use.
|
|
377
|
+
*/
|
|
378
|
+
private createRawCompletion;
|
|
379
|
+
/**
|
|
380
|
+
* Una chiamata all'endpoint Decisions (`decisions.ts`) — stessa contabilità di `createRawCompletion`:
|
|
381
|
+
* prenota il budget del tenant prima, regola il costo reale dopo. È il punto unico da cui passano
|
|
382
|
+
* sia `ai.decisions.evaluate()` sia i tool costruiti con `ai.decisions.tool()`, cosicché una
|
|
383
|
+
* decisione chiamata da dentro un loop di tool-use paghi sullo stesso conto delle chiamate dirette.
|
|
384
|
+
*/
|
|
385
|
+
private evaluateDecision;
|
|
386
|
+
/**
|
|
387
|
+
* Una chiamata all'endpoint TTS (`speech.ts`) — la stessa contabilità di `createRawCompletion`
|
|
388
|
+
* **meno la regolazione del costo**, e la differenza va detta: la risposta è audio e non porta
|
|
389
|
+
* un `usage`; il costo si legge da `GET /generation` solo dopo circa cinque secondi. Aspettarli
|
|
390
|
+
* dentro la chiamata la rallenterebbe per un numero che serve soltanto al preavviso di
|
|
391
|
+
* `lowBalance` — l'addebito vero lo misura il ledger dall'usage della chiave, qui come per una
|
|
392
|
+
* chat. Quindi `unsettled` non si tocca, e il risultato porta l'id della generazione e non una
|
|
393
|
+
* stima.
|
|
394
|
+
*/
|
|
395
|
+
private createSpeech;
|
|
396
|
+
/**
|
|
397
|
+
* Chiamata autenticata al control plane con la project key.
|
|
398
|
+
*
|
|
399
|
+
* Il codice d'errore arriva dal Cloud e si propaga così com'è (`ALLOWANCE_EXHAUSTED`,
|
|
400
|
+
* `CONTO_KEY_EXISTS`, `SCOPE_FORBIDDEN`…): tradurlo in un codice generico farebbe perdere
|
|
401
|
+
* al chiamante l'unica informazione che gli dice cosa fare.
|
|
402
|
+
*/
|
|
403
|
+
private api;
|
|
404
|
+
/**
|
|
405
|
+
* Salda subito il consumo residuo di un tenant senza avviare una nuova chiamata.
|
|
406
|
+
* Da invocare a fine sessione, così l'ultimo costo non resta in sospeso fino al giro
|
|
407
|
+
* successivo (la misura del provider resta comunque la fonte di verità).
|
|
408
|
+
*/
|
|
409
|
+
flushBudget(userKey: string): Promise<void>;
|
|
410
|
+
}
|