@tanstack-ai-browser/apfel 0.0.1 → 0.1.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/README.md +34 -4
- package/dist/adapter.d.ts +49 -42
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +132 -51
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/tool-repair.d.ts +56 -0
- package/dist/tool-repair.d.ts.map +1 -0
- package/dist/tool-repair.js +252 -0
- package/package.json +2 -2
- package/src/adapter.ts +185 -88
- package/src/index.ts +8 -3
- package/src/tool-repair.ts +321 -0
package/src/adapter.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A TanStack AI text adapter for Apple's on-device Foundation Model, served
|
|
3
|
-
* by apfel's OpenAI-compatible server
|
|
4
|
-
* involved: the browser streams straight from
|
|
3
|
+
* by apfel's OpenAI-compatible server on the user's Mac. No backend
|
|
4
|
+
* involved: the browser streams straight from it. Hand it to `chat()`
|
|
5
5
|
* like any hosted model's adapter: `chat()` runs tools and the agent loop,
|
|
6
6
|
* and each `chatStream` call here is one completion from apfel, streamed
|
|
7
7
|
* back as AG-UI events.
|
|
8
8
|
*/
|
|
9
|
-
import { EventType, normalizeSystemPrompts } from "@tanstack/ai"
|
|
9
|
+
import { buildBaseUsage, EventType, normalizeSystemPrompts } from "@tanstack/ai"
|
|
10
10
|
import type {
|
|
11
11
|
AdapterYieldChunk,
|
|
12
12
|
ModelMessage,
|
|
13
13
|
TextOptions,
|
|
14
|
+
TokenUsage,
|
|
14
15
|
Tool
|
|
15
16
|
} from "@tanstack/ai"
|
|
16
17
|
import { BaseTextAdapter } from "@tanstack/ai/adapters"
|
|
@@ -24,46 +25,46 @@ import {
|
|
|
24
25
|
createToolCallSplitter,
|
|
25
26
|
parseJsonToolCalls
|
|
26
27
|
} from "./tool-calls.js"
|
|
28
|
+
import { matchToolName, repairToolArguments } from "./tool-repair.js"
|
|
29
|
+
import type { GuidedRequest } from "./tool-repair.js"
|
|
27
30
|
|
|
28
|
-
/** Where
|
|
31
|
+
/** Where apfel's server listens by default. */
|
|
29
32
|
export const APFEL_BASE_URL = "http://127.0.0.1:11434/v1"
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
|
|
34
|
+
/** The models apfel serves: Apple's on-device Foundation Model. */
|
|
35
|
+
export const APFEL_TEXT_MODELS = ["apple-foundationmodel"] as const
|
|
36
|
+
|
|
37
|
+
/** A model apfel serves. */
|
|
38
|
+
export type ApfelTextModel = (typeof APFEL_TEXT_MODELS)[number] | (string & {})
|
|
39
|
+
|
|
40
|
+
export type ApfelTextConfig = {
|
|
41
|
+
/** apfel's OpenAI-compatible API, up to `/v1`. `APFEL_BASE_URL` by default. */
|
|
42
|
+
baseURL?: string
|
|
43
|
+
/** Headers sent with every request, such as apfel's bearer token. */
|
|
44
|
+
defaultHeaders?: Record<string, string>
|
|
45
|
+
}
|
|
32
46
|
|
|
33
47
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
48
|
+
* Timings of one model call, in milliseconds, in `RUN_FINISHED`'s
|
|
49
|
+
* `usage.providerUsageDetails`. The adapter measures them as the stream
|
|
50
|
+
* arrives; apfel itself reports token counts only.
|
|
37
51
|
*/
|
|
38
|
-
export type
|
|
39
|
-
/**
|
|
40
|
-
inputTokens?: number
|
|
41
|
-
/** Prompt tokens served from a cache, if apfel reports them. */
|
|
42
|
-
cachedTokens?: number
|
|
43
|
-
/** Tokens the model generated. */
|
|
44
|
-
outputTokens?: number
|
|
45
|
-
/** From sending the request to the first generated token, in ms. */
|
|
52
|
+
export type ApfelProviderUsageDetails = {
|
|
53
|
+
/** From sending the request to the first generated text. */
|
|
46
54
|
firstTokenMs?: number
|
|
47
|
-
/**
|
|
48
|
-
decodeTokens?: number
|
|
49
|
-
/** From the first generated chunk to the last, in ms. */
|
|
55
|
+
/** From the first generated text to the last. */
|
|
50
56
|
decodeMs?: number
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export type ApfelOptions = {
|
|
54
|
-
/** apfel's OpenAI-compatible API, up to `/v1`. */
|
|
55
|
-
baseUrl?: string
|
|
56
|
-
/** The model id to ask apfel for. */
|
|
57
|
-
model?: string
|
|
58
57
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
58
|
+
* Tokens generated after the first chunk, estimated: apfel streams a few
|
|
59
|
+
* tokens per chunk, and the first chunk's share of the characters stands
|
|
60
|
+
* in for its share of the tokens.
|
|
61
|
+
*/
|
|
62
|
+
decodeTokens?: number
|
|
63
|
+
/**
|
|
64
|
+
* Time spent rewriting the tool calls' arguments under their schemas,
|
|
65
|
+
* after the reply ended (see the README's "Tool calls").
|
|
62
66
|
*/
|
|
63
|
-
|
|
64
|
-
stats: ApfelStats,
|
|
65
|
-
run: { threadId: string; runId: string; callId: string; done: boolean }
|
|
66
|
-
) => void
|
|
67
|
+
toolRepairMs?: number
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
/**
|
|
@@ -283,27 +284,34 @@ async function errorBody(response: Response) {
|
|
|
283
284
|
return raw.slice(0, 200)
|
|
284
285
|
}
|
|
285
286
|
|
|
286
|
-
|
|
287
|
+
/** apfel's token counts as TanStack AI usage, with the call's timings. */
|
|
288
|
+
function usageOf(
|
|
289
|
+
usage: OpenAIUsage | null | undefined,
|
|
290
|
+
details?: ApfelProviderUsageDetails
|
|
291
|
+
): TokenUsage | undefined {
|
|
287
292
|
if (
|
|
288
293
|
usage?.prompt_tokens === undefined ||
|
|
289
294
|
usage.completion_tokens === undefined
|
|
290
295
|
) {
|
|
291
296
|
return undefined
|
|
292
297
|
}
|
|
293
|
-
const
|
|
294
|
-
return {
|
|
298
|
+
const result = buildBaseUsage<ApfelProviderUsageDetails>({
|
|
295
299
|
promptTokens: usage.prompt_tokens,
|
|
296
300
|
completionTokens: usage.completion_tokens,
|
|
297
|
-
totalTokens: usage.prompt_tokens + usage.completion_tokens
|
|
298
|
-
|
|
301
|
+
totalTokens: usage.prompt_tokens + usage.completion_tokens
|
|
302
|
+
})
|
|
303
|
+
const cachedTokens = usage.prompt_tokens_details?.cached_tokens
|
|
304
|
+
if (cachedTokens) result.promptTokensDetails = { cachedTokens }
|
|
305
|
+
if (details && Object.keys(details).length > 0) {
|
|
306
|
+
result.providerUsageDetails = details
|
|
299
307
|
}
|
|
308
|
+
return result
|
|
300
309
|
}
|
|
301
310
|
|
|
302
311
|
const errorText = (error: unknown) =>
|
|
303
312
|
error instanceof Error ? error.message : String(error)
|
|
304
313
|
|
|
305
|
-
const unreachable = (
|
|
306
|
-
`Couldn't reach Apple's on-device model at ${baseUrl}. Start it with \`apfel --serve --cors\`.`
|
|
314
|
+
const unreachable = (baseURL: string) => `Couldn't reach apfel at ${baseURL}.`
|
|
307
315
|
|
|
308
316
|
let ids = 0
|
|
309
317
|
const nextId = (prefix: string) =>
|
|
@@ -311,8 +319,8 @@ const nextId = (prefix: string) =>
|
|
|
311
319
|
|
|
312
320
|
type StreamedCall = { name: string; arguments: string }
|
|
313
321
|
|
|
314
|
-
export class ApfelTextAdapter extends BaseTextAdapter<
|
|
315
|
-
|
|
322
|
+
export class ApfelTextAdapter<TModel extends string> extends BaseTextAdapter<
|
|
323
|
+
TModel,
|
|
316
324
|
ApfelModelOptions,
|
|
317
325
|
readonly ["text"],
|
|
318
326
|
MetadataByModality,
|
|
@@ -321,15 +329,21 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
321
329
|
never,
|
|
322
330
|
ApfelReasoning
|
|
323
331
|
> {
|
|
332
|
+
override readonly kind = "text" as const
|
|
324
333
|
readonly name = "apfel" as const
|
|
325
|
-
override readonly
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
334
|
+
override readonly provider = "apfel" as const
|
|
335
|
+
override readonly api = "openai-completions" as const
|
|
336
|
+
override readonly inputModalities = ["text"] as const
|
|
337
|
+
private readonly baseURL: string
|
|
338
|
+
private readonly headers: Record<string, string>
|
|
339
|
+
|
|
340
|
+
constructor(config: ApfelTextConfig, model: TModel) {
|
|
341
|
+
super({}, model)
|
|
342
|
+
this.baseURL = (config.baseURL ?? APFEL_BASE_URL).replace(/\/+$/, "")
|
|
343
|
+
this.headers = {
|
|
344
|
+
...config.defaultHeaders,
|
|
345
|
+
"Content-Type": "application/json"
|
|
346
|
+
}
|
|
333
347
|
}
|
|
334
348
|
|
|
335
349
|
async *chatStream(
|
|
@@ -337,7 +351,6 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
337
351
|
): AsyncIterable<AdapterYieldChunk> {
|
|
338
352
|
const runId = options.runId ?? nextId("run")
|
|
339
353
|
const threadId = options.threadId
|
|
340
|
-
const callId = nextId("call")
|
|
341
354
|
const messageId = nextId("msg")
|
|
342
355
|
const reasoningId = nextId("reasoning")
|
|
343
356
|
const model = this.model
|
|
@@ -367,7 +380,7 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
367
380
|
const tools = toOpenAITools(options.tools)
|
|
368
381
|
const toolNames = new Set(tools.map((tool) => tool.function.name))
|
|
369
382
|
const send = options.wrapFetch ? options.wrapFetch(fetch) : fetch
|
|
370
|
-
const url = `${this.
|
|
383
|
+
const url = `${this.baseURL}/chat/completions`
|
|
371
384
|
logger.request(
|
|
372
385
|
`activity=chat provider=apfel model=${model} messages=${options.messages.length} tools=${tools.length} stream=true`,
|
|
373
386
|
{ provider: "apfel", model }
|
|
@@ -377,7 +390,7 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
377
390
|
try {
|
|
378
391
|
response = await send(url, {
|
|
379
392
|
method: "POST",
|
|
380
|
-
headers:
|
|
393
|
+
headers: this.headers,
|
|
381
394
|
body: JSON.stringify({
|
|
382
395
|
...options.modelOptions,
|
|
383
396
|
model,
|
|
@@ -401,7 +414,7 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
401
414
|
error,
|
|
402
415
|
source: "apfel.chatStream"
|
|
403
416
|
})
|
|
404
|
-
yield runError(unreachable(this.
|
|
417
|
+
yield runError(unreachable(this.baseURL))
|
|
405
418
|
return
|
|
406
419
|
}
|
|
407
420
|
if (!response.ok || !response.body) {
|
|
@@ -533,11 +546,6 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
533
546
|
if (!timing.first) {
|
|
534
547
|
timing.first = timing.last
|
|
535
548
|
timing.firstChars = chars
|
|
536
|
-
// Token counts come only with the usage at the end.
|
|
537
|
-
this.onStats?.(
|
|
538
|
-
{ firstTokenMs: timing.first - sentAt },
|
|
539
|
-
{ threadId, runId, callId, done: false }
|
|
540
|
-
)
|
|
541
549
|
}
|
|
542
550
|
timing.chars += chars
|
|
543
551
|
}
|
|
@@ -567,23 +575,17 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
567
575
|
}
|
|
568
576
|
|
|
569
577
|
const outputTokens = usage?.completion_tokens
|
|
570
|
-
|
|
571
|
-
{
|
|
572
|
-
inputTokens: usage?.prompt_tokens,
|
|
573
|
-
cachedTokens: usage?.prompt_tokens_details?.cached_tokens,
|
|
574
|
-
outputTokens,
|
|
575
|
-
...(timing.first && {
|
|
578
|
+
const details: ApfelProviderUsageDetails = timing.first
|
|
579
|
+
? {
|
|
576
580
|
firstTokenMs: timing.first - sentAt,
|
|
581
|
+
decodeMs: timing.last - timing.first,
|
|
577
582
|
...(outputTokens !== undefined && {
|
|
578
583
|
decodeTokens:
|
|
579
584
|
(outputTokens * (timing.chars - timing.firstChars)) /
|
|
580
|
-
timing.chars
|
|
581
|
-
decodeMs: timing.last - timing.first
|
|
585
|
+
timing.chars
|
|
582
586
|
})
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
{ threadId, runId, callId, done: true }
|
|
586
|
-
)
|
|
587
|
+
}
|
|
588
|
+
: {}
|
|
587
589
|
|
|
588
590
|
endReasoning()
|
|
589
591
|
const { text: rest, markup } = splitter.flush()
|
|
@@ -604,11 +606,13 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
604
606
|
name: call.name,
|
|
605
607
|
arguments: JSON.stringify(call.args)
|
|
606
608
|
}))
|
|
607
|
-
]
|
|
609
|
+
].map((call) => ({
|
|
610
|
+
...call,
|
|
611
|
+
name: matchToolName(call.name, toolNames) ?? call.name
|
|
612
|
+
}))
|
|
613
|
+
let repairMs = 0
|
|
608
614
|
for (const [index, call] of calls.entries()) {
|
|
609
615
|
const toolCallId = nextId("tool")
|
|
610
|
-
const args = call.arguments || "{}"
|
|
611
|
-
const input = parseArguments(args)
|
|
612
616
|
yield {
|
|
613
617
|
type: EventType.TOOL_CALL_START,
|
|
614
618
|
toolCallId,
|
|
@@ -618,6 +622,21 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
618
622
|
model,
|
|
619
623
|
timestamp: timestamp()
|
|
620
624
|
}
|
|
625
|
+
const tool = options.tools?.find((t) => t.name === call.name)
|
|
626
|
+
const repairStart = performance.now()
|
|
627
|
+
const repaired = tool
|
|
628
|
+
? await this.repairArguments(
|
|
629
|
+
tool,
|
|
630
|
+
call.arguments,
|
|
631
|
+
options,
|
|
632
|
+
send,
|
|
633
|
+
signal
|
|
634
|
+
)
|
|
635
|
+
: undefined
|
|
636
|
+
repairMs += performance.now() - repairStart
|
|
637
|
+
if (signal?.aborted) return
|
|
638
|
+
const args = repaired ? JSON.stringify(repaired) : call.arguments || "{}"
|
|
639
|
+
const input = repaired ?? parseArguments(args)
|
|
621
640
|
yield {
|
|
622
641
|
type: EventType.TOOL_CALL_ARGS,
|
|
623
642
|
toolCallId,
|
|
@@ -640,7 +659,8 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
640
659
|
endText()
|
|
641
660
|
yield* events.splice(0)
|
|
642
661
|
|
|
643
|
-
|
|
662
|
+
if (repairMs > 0) details.toolRepairMs = repairMs
|
|
663
|
+
const finishUsage = usageOf(usage, details)
|
|
644
664
|
yield {
|
|
645
665
|
type: EventType.RUN_FINISHED,
|
|
646
666
|
runId,
|
|
@@ -657,6 +677,75 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
657
677
|
}
|
|
658
678
|
}
|
|
659
679
|
|
|
680
|
+
/**
|
|
681
|
+
* A tool call's arguments, written again under the tool's schema with
|
|
682
|
+
* guided generation. Undefined if the tool takes no arguments or a request
|
|
683
|
+
* failed: the model's own arguments are used then.
|
|
684
|
+
*/
|
|
685
|
+
private async repairArguments(
|
|
686
|
+
tool: Tool,
|
|
687
|
+
draft: string,
|
|
688
|
+
options: TextOptions<ApfelModelOptions>,
|
|
689
|
+
send: typeof fetch,
|
|
690
|
+
signal: AbortSignal | undefined
|
|
691
|
+
) {
|
|
692
|
+
const system = normalizeSystemPrompts(options.systemPrompts)
|
|
693
|
+
.map((p) => p.content)
|
|
694
|
+
.join("\n\n")
|
|
695
|
+
const messages = toOpenAIMessages(options.messages)
|
|
696
|
+
const complete = async ({ instructions, schema, name }: GuidedRequest) => {
|
|
697
|
+
options.logger.request(
|
|
698
|
+
`activity=tool-repair provider=apfel model=${this.model} tool=${tool.name} step=${name}`,
|
|
699
|
+
{ provider: "apfel", model: this.model }
|
|
700
|
+
)
|
|
701
|
+
const response = await send(`${this.baseURL}/chat/completions`, {
|
|
702
|
+
method: "POST",
|
|
703
|
+
headers: this.headers,
|
|
704
|
+
body: JSON.stringify({
|
|
705
|
+
...options.modelOptions,
|
|
706
|
+
model: this.model,
|
|
707
|
+
// apfel takes one system message, and the conversation must end
|
|
708
|
+
// with the user or a tool result: the instructions go first.
|
|
709
|
+
messages: [
|
|
710
|
+
{
|
|
711
|
+
role: "system",
|
|
712
|
+
content: system
|
|
713
|
+
? `${instructions}\n\nThe assistant's instructions, for context:\n${system}`
|
|
714
|
+
: instructions
|
|
715
|
+
},
|
|
716
|
+
...messages
|
|
717
|
+
],
|
|
718
|
+
response_format: {
|
|
719
|
+
type: "json_schema",
|
|
720
|
+
json_schema: { name, schema }
|
|
721
|
+
}
|
|
722
|
+
}),
|
|
723
|
+
signal
|
|
724
|
+
})
|
|
725
|
+
if (!response.ok) {
|
|
726
|
+
throw new Error(
|
|
727
|
+
`apfel returned HTTP ${response.status}: ${await errorBody(response)}`
|
|
728
|
+
)
|
|
729
|
+
}
|
|
730
|
+
const completion = (await response.json()) as OpenAICompletion
|
|
731
|
+
const text = (completion.choices?.[0]?.message?.content ?? "").trim()
|
|
732
|
+
return JSON.parse(
|
|
733
|
+
text.replace(/^```(?:json)?\s*|\s*```$/g, "")
|
|
734
|
+
) as unknown
|
|
735
|
+
}
|
|
736
|
+
try {
|
|
737
|
+
return await repairToolArguments(tool, complete, draft)
|
|
738
|
+
} catch (error) {
|
|
739
|
+
if (!signal?.aborted) {
|
|
740
|
+
options.logger.errors("apfel tool-call repair failed", {
|
|
741
|
+
error,
|
|
742
|
+
source: "apfel.repairArguments"
|
|
743
|
+
})
|
|
744
|
+
}
|
|
745
|
+
return undefined
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
660
749
|
/**
|
|
661
750
|
* A JSON answer for `chat({ outputSchema })`, from apfel's JSON mode:
|
|
662
751
|
* `response_format: json_schema` constrains Apple's model to the schema
|
|
@@ -674,9 +763,9 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
674
763
|
)
|
|
675
764
|
let response: Response
|
|
676
765
|
try {
|
|
677
|
-
response = await send(`${this.
|
|
766
|
+
response = await send(`${this.baseURL}/chat/completions`, {
|
|
678
767
|
method: "POST",
|
|
679
|
-
headers:
|
|
768
|
+
headers: this.headers,
|
|
680
769
|
body: JSON.stringify({
|
|
681
770
|
...chatOptions.modelOptions,
|
|
682
771
|
model,
|
|
@@ -700,7 +789,7 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
700
789
|
source: "apfel.structuredOutput"
|
|
701
790
|
})
|
|
702
791
|
if (chatOptions.abortController?.signal.aborted) throw error
|
|
703
|
-
throw new Error(unreachable(this.
|
|
792
|
+
throw new Error(unreachable(this.baseURL))
|
|
704
793
|
}
|
|
705
794
|
if (!response.ok) {
|
|
706
795
|
throw new Error(
|
|
@@ -737,16 +826,23 @@ export class ApfelTextAdapter extends BaseTextAdapter<
|
|
|
737
826
|
}
|
|
738
827
|
|
|
739
828
|
/**
|
|
740
|
-
* A TanStack AI adapter for Apple's on-device Foundation Model
|
|
741
|
-
*
|
|
829
|
+
* A TanStack AI text adapter for Apple's on-device Foundation Model, served
|
|
830
|
+
* by apfel.
|
|
742
831
|
*
|
|
743
832
|
* @example
|
|
744
833
|
* ```ts
|
|
745
|
-
* chat({
|
|
834
|
+
* chat({
|
|
835
|
+
* adapter: apfelText("apple-foundationmodel", { baseURL: APFEL_BASE_URL }),
|
|
836
|
+
* messages,
|
|
837
|
+
* threadId
|
|
838
|
+
* })
|
|
746
839
|
* ```
|
|
747
840
|
*/
|
|
748
|
-
export function
|
|
749
|
-
|
|
841
|
+
export function apfelText<TModel extends ApfelTextModel>(
|
|
842
|
+
model: TModel,
|
|
843
|
+
config: ApfelTextConfig = {}
|
|
844
|
+
): ApfelTextAdapter<TModel> {
|
|
845
|
+
return new ApfelTextAdapter(config, model)
|
|
750
846
|
}
|
|
751
847
|
|
|
752
848
|
/**
|
|
@@ -754,12 +850,13 @@ export function apfel(options?: ApfelOptions) {
|
|
|
754
850
|
* lists it within 10 seconds.
|
|
755
851
|
*/
|
|
756
852
|
export async function isApfelAvailable(
|
|
757
|
-
|
|
853
|
+
model: ApfelTextModel,
|
|
854
|
+
config: ApfelTextConfig = {}
|
|
758
855
|
) {
|
|
759
|
-
const
|
|
760
|
-
const model = options.model ?? APFEL_MODEL
|
|
856
|
+
const baseURL = (config.baseURL ?? APFEL_BASE_URL).replace(/\/+$/, "")
|
|
761
857
|
try {
|
|
762
|
-
const response = await fetch(`${
|
|
858
|
+
const response = await fetch(`${baseURL}/models`, {
|
|
859
|
+
headers: { ...config.defaultHeaders },
|
|
763
860
|
signal: AbortSignal.timeout(10_000)
|
|
764
861
|
})
|
|
765
862
|
if (!response.ok) return false
|
package/src/index.ts
CHANGED
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export {
|
|
5
5
|
APFEL_BASE_URL,
|
|
6
|
-
|
|
6
|
+
APFEL_TEXT_MODELS,
|
|
7
7
|
ApfelTextAdapter,
|
|
8
|
-
|
|
8
|
+
apfelText,
|
|
9
9
|
isApfelAvailable
|
|
10
10
|
} from "./adapter.js"
|
|
11
|
-
export type {
|
|
11
|
+
export type {
|
|
12
|
+
ApfelModelOptions,
|
|
13
|
+
ApfelProviderUsageDetails,
|
|
14
|
+
ApfelTextConfig,
|
|
15
|
+
ApfelTextModel
|
|
16
|
+
} from "./adapter.js"
|