@tanstack-ai-browser/apfel 0.0.1 → 0.2.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 +36 -5
- package/dist/adapter.d.ts +33 -79
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +45 -574
- package/dist/guided-schema.d.ts +20 -0
- package/dist/guided-schema.d.ts.map +1 -0
- package/dist/guided-schema.js +136 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/model-meta.d.ts +23 -0
- package/dist/model-meta.d.ts.map +1 -0
- package/dist/model-meta.js +20 -0
- 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/dist/tools.d.ts +68 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +469 -0
- package/package.json +11 -4
- package/src/adapter.test.ts +81 -0
- package/src/adapter.ts +80 -745
- package/src/availability.test.ts +61 -0
- package/src/index.ts +5 -8
- package/src/model-meta.ts +37 -0
- package/src/tool-calls.ts +0 -134
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @tanstack-ai-browser/apfel
|
|
2
2
|
|
|
3
|
-
A [TanStack AI](https://tanstack.com/ai) adapter for Apple's on-device Foundation Model, served on your Mac by [apfel](https://apfel.franzai.com)'s OpenAI-compatible server. The browser streams straight from
|
|
3
|
+
A [TanStack AI](https://tanstack.com/ai) adapter for Apple's on-device Foundation Model, served on your Mac by [apfel](https://apfel.franzai.com)'s OpenAI-compatible server. The browser streams straight from apfel.
|
|
4
|
+
|
|
5
|
+
It's a thin pass-through: TanStack's OpenAI-compatible Chat Completions adapter (`@tanstack/ai-openai/compatible`) pointed at apfel. Tool calls go to apfel as `tools` and come back as apfel parses them; structured output goes as `response_format`, which apfel holds the model to with Apple's guided generation. The adapter adds an availability check and renames `modelOptions` for the request.
|
|
4
6
|
|
|
5
7
|
```sh
|
|
6
8
|
npm install @tanstack-ai-browser/apfel @tanstack/ai
|
|
@@ -8,11 +10,40 @@ npm install @tanstack-ai-browser/apfel @tanstack/ai
|
|
|
8
10
|
|
|
9
11
|
```ts
|
|
10
12
|
import { chat } from "@tanstack/ai"
|
|
11
|
-
import {
|
|
13
|
+
import { apfelText } from "@tanstack-ai-browser/apfel"
|
|
14
|
+
|
|
15
|
+
const adapter = apfelText()
|
|
12
16
|
|
|
13
|
-
if (await
|
|
14
|
-
const stream = chat({ adapter
|
|
17
|
+
if ((await adapter.availability()) === "available") {
|
|
18
|
+
const stream = chat({ adapter, messages, threadId, tools })
|
|
15
19
|
}
|
|
16
20
|
```
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
Start apfel with `apfel --serve --cors`.
|
|
23
|
+
|
|
24
|
+
## Images
|
|
25
|
+
|
|
26
|
+
Image parts go to apfel as OpenAI `image_url` parts, so `adapter.inputModalities` lists `"image"` for Apple's model. Apple's model reads images natively on macOS 27 and later; on older macOS, apfel passes it only the text and labels it reads from the image.
|
|
27
|
+
|
|
28
|
+
## Config
|
|
29
|
+
|
|
30
|
+
- `baseURL`: apfel's API, up to `/v1`. It defaults to `http://127.0.0.1:11434/v1`.
|
|
31
|
+
- `defaultHeaders`: headers for every request, such as a bearer token.
|
|
32
|
+
|
|
33
|
+
## Availability
|
|
34
|
+
|
|
35
|
+
- `adapter.availability()` resolves to `"available"` when apfel lists the model within 10 seconds, and to `"unavailable"` otherwise.
|
|
36
|
+
- `adapter.create({ signal? })` makes the same check. It resolves to the adapter, or rejects. Nothing loads, since apfel runs the model outside the page.
|
|
37
|
+
|
|
38
|
+
## Model options
|
|
39
|
+
|
|
40
|
+
`modelOptions` are the request fields apfel takes, with OpenAI's names, sent as given (as TanStack's adapters do with their provider's fields): `max_tokens`, `temperature`, `top_p` and `seed`.
|
|
41
|
+
|
|
42
|
+
## Exports
|
|
43
|
+
|
|
44
|
+
- `@tanstack-ai-browser/apfel`:
|
|
45
|
+
- `apfelText(config?)` for Apple's model, or `apfelText(model, config?)` for another model apfel serves
|
|
46
|
+
- the types `ApfelConfig`, `ApfelModel`, `ApfelTextAdapter`, `ApfelTextModelOptions` and `Availability`
|
|
47
|
+
- the catalog: `APFEL_CHAT_MODELS` and `APFEL_MODEL_META`, with the types `ApfelChatModel` and `ApfelModelMeta`
|
|
48
|
+
|
|
49
|
+
**Status:** early (0.x). The API may change.
|
package/dist/adapter.d.ts
CHANGED
|
@@ -1,91 +1,45 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* A model call's token counts and timings. apfel reports token counts only
|
|
10
|
-
* in the usage at the end of the stream; counts it doesn't report are left
|
|
11
|
-
* out.
|
|
12
|
-
*/
|
|
13
|
-
export type ApfelStats = {
|
|
14
|
-
/** Tokens of the prompt. */
|
|
15
|
-
inputTokens?: number;
|
|
16
|
-
/** Prompt tokens served from a cache, if apfel reports them. */
|
|
17
|
-
cachedTokens?: number;
|
|
18
|
-
/** Tokens the model generated. */
|
|
19
|
-
outputTokens?: number;
|
|
20
|
-
/** From sending the request to the first generated token, in ms. */
|
|
21
|
-
firstTokenMs?: number;
|
|
22
|
-
/** Tokens generated after the first chunk (estimated, see below). */
|
|
23
|
-
decodeTokens?: number;
|
|
24
|
-
/** From the first generated chunk to the last, in ms. */
|
|
25
|
-
decodeMs?: number;
|
|
26
|
-
};
|
|
27
|
-
export type ApfelOptions = {
|
|
28
|
-
/** apfel's OpenAI-compatible API, up to `/v1`. */
|
|
29
|
-
baseUrl?: string;
|
|
30
|
-
/** The model id to ask apfel for. */
|
|
31
|
-
model?: string;
|
|
32
|
-
/**
|
|
33
|
-
* The call's stats at its first token (`done: false`, time to first token
|
|
34
|
-
* only) and when it ends (`done: true`). `callId` is unique per model
|
|
35
|
-
* call; `chat()` makes one per step of its agent loop.
|
|
36
|
-
*/
|
|
37
|
-
onStats?: (stats: ApfelStats, run: {
|
|
38
|
-
threadId: string;
|
|
39
|
-
runId: string;
|
|
40
|
-
callId: string;
|
|
41
|
-
done: boolean;
|
|
42
|
-
}) => void;
|
|
1
|
+
import type { OpenAICompatibleChatAdapter } from "@tanstack/ai-openai/compatible";
|
|
2
|
+
import type { ApfelChatModel } from "./model-meta.js";
|
|
3
|
+
export type ApfelConfig = {
|
|
4
|
+
/** apfel's OpenAI-compatible API, up to `/v1`. `http://127.0.0.1:11434/v1` by default. */
|
|
5
|
+
baseURL?: string;
|
|
6
|
+
/** Headers sent with every request, such as apfel's bearer token. */
|
|
7
|
+
defaultHeaders?: Record<string, string>;
|
|
43
8
|
};
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*/
|
|
48
|
-
export type ApfelModelOptions = {
|
|
49
|
-
temperature?: number;
|
|
9
|
+
/** Per-call `modelOptions`: the request fields apfel takes, with OpenAI's names, sent as given. */
|
|
10
|
+
export type ApfelTextModelOptions = {
|
|
11
|
+
/** Most tokens to generate. */
|
|
50
12
|
max_tokens?: number;
|
|
13
|
+
temperature?: number;
|
|
14
|
+
top_p?: number;
|
|
51
15
|
seed?: number;
|
|
52
16
|
};
|
|
53
|
-
|
|
54
|
-
type
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
17
|
+
/** The Prompt API's availability values; apfel's model is "available" or "unavailable". */
|
|
18
|
+
export type Availability = "unavailable" | "downloadable" | "downloading" | "available";
|
|
19
|
+
/** TanStack's OpenAI-compatible adapter for apfel, with apfel's availability. */
|
|
20
|
+
export type ApfelTextAdapter = OpenAICompatibleChatAdapter<ApfelModel, ApfelTextModelOptions> & {
|
|
21
|
+
/** Text and images for Apple's model; not known for another model. */
|
|
22
|
+
readonly inputModalities: ReadonlyArray<"text" | "image"> | undefined;
|
|
23
|
+
/** "available" if apfel answers and serves the model, else "unavailable". */
|
|
24
|
+
availability(signal?: AbortSignal): Promise<Availability>;
|
|
25
|
+
/** Resolves to this adapter if apfel serves the model, else rejects. Nothing loads. */
|
|
26
|
+
create(options?: {
|
|
27
|
+
signal?: AbortSignal;
|
|
28
|
+
}): Promise<ApfelTextAdapter>;
|
|
60
29
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
readonly api: "apfel";
|
|
64
|
-
private readonly baseUrl;
|
|
65
|
-
private readonly onStats;
|
|
66
|
-
constructor(options?: ApfelOptions);
|
|
67
|
-
chatStream(options: TextOptions<ApfelModelOptions>): AsyncIterable<AdapterYieldChunk>;
|
|
68
|
-
/**
|
|
69
|
-
* A JSON answer for `chat({ outputSchema })`, from apfel's JSON mode:
|
|
70
|
-
* `response_format: json_schema` constrains Apple's model to the schema
|
|
71
|
-
* (guided generation). `chat()` validates the result.
|
|
72
|
-
*/
|
|
73
|
-
structuredOutput(options: StructuredOutputOptions<ApfelModelOptions>): Promise<StructuredOutputResult<unknown>>;
|
|
74
|
-
}
|
|
30
|
+
/** A model apfel serves: Apple's ("apple-foundationmodel", the default), or any other name. */
|
|
31
|
+
export type ApfelModel = ApfelChatModel | (string & {});
|
|
75
32
|
/**
|
|
76
|
-
*
|
|
77
|
-
* apfel (`apfel --serve --cors`).
|
|
33
|
+
* An adapter for a model apfel serves: Apple's on-device model by default.
|
|
78
34
|
*
|
|
79
35
|
* @example
|
|
80
36
|
* ```ts
|
|
81
|
-
*
|
|
37
|
+
* const adapter = apfelText()
|
|
38
|
+
* if ((await adapter.availability()) === "available") {
|
|
39
|
+
* chat({ adapter, messages, tools })
|
|
40
|
+
* }
|
|
82
41
|
* ```
|
|
83
42
|
*/
|
|
84
|
-
export declare function
|
|
85
|
-
|
|
86
|
-
* Whether apfel is running and serves the model: true if `GET /v1/models`
|
|
87
|
-
* lists it within 10 seconds.
|
|
88
|
-
*/
|
|
89
|
-
export declare function isApfelAvailable(options?: Pick<ApfelOptions, "baseUrl" | "model">): Promise<boolean>;
|
|
90
|
-
export {};
|
|
43
|
+
export declare function apfelText(config?: ApfelConfig): ApfelTextAdapter;
|
|
44
|
+
export declare function apfelText(model: ApfelModel, config?: ApfelConfig): ApfelTextAdapter;
|
|
91
45
|
//# sourceMappingURL=adapter.d.ts.map
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AAGjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD,MAAM,MAAM,WAAW,GAAG;IACxB,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qEAAqE;IACrE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACxC,CAAA;AAED,mGAAmG;AACnG,MAAM,MAAM,qBAAqB,GAAG;IAClC,+BAA+B;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,2FAA2F;AAC3F,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,WAAW,CAAA;AAEvF,iFAAiF;AACjF,MAAM,MAAM,gBAAgB,GAAG,2BAA2B,CAAC,UAAU,EAAE,qBAAqB,CAAC,GAAG;IAC9F,sEAAsE;IACtE,QAAQ,CAAC,eAAe,EAAE,aAAa,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,SAAS,CAAA;IACrE,6EAA6E;IAC7E,YAAY,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACzD,uFAAuF;IACvF,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;CACtE,CAAA;AAID,+FAA+F;AAC/F,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAA;AAEvD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,gBAAgB,CAAA;AACjE,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,gBAAgB,CAAA"}
|