@tanstack-ai-browser/apfel 0.1.0 → 0.2.1
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 +29 -28
- package/dist/adapter.d.ts +30 -83
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +53 -654
- 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/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 +89 -837
- package/src/availability.test.ts +61 -0
- package/src/index.ts +5 -13
- package/src/model-meta.ts +37 -0
- package/src/tool-calls.ts +0 -134
- package/src/tool-repair.ts +0 -321
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 apfel
|
|
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,41 +10,40 @@ npm install @tanstack-ai-browser/apfel @tanstack/ai
|
|
|
8
10
|
|
|
9
11
|
```ts
|
|
10
12
|
import { chat } from "@tanstack/ai"
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const config = { baseURL: APFEL_BASE_URL }
|
|
18
|
-
|
|
19
|
-
if (await isApfelAvailable("apple-foundationmodel", config)) {
|
|
20
|
-
const stream = chat({
|
|
21
|
-
adapter: apfelText("apple-foundationmodel", config),
|
|
22
|
-
messages,
|
|
23
|
-
threadId
|
|
24
|
-
})
|
|
13
|
+
import { apfelText } from "@tanstack-ai-browser/apfel"
|
|
14
|
+
|
|
15
|
+
const adapter = apfelText()
|
|
16
|
+
|
|
17
|
+
if ((await adapter.availability()) === "available") {
|
|
18
|
+
const stream = chat({ adapter, messages, threadId, tools })
|
|
25
19
|
}
|
|
26
20
|
```
|
|
27
21
|
|
|
28
|
-
|
|
22
|
+
Start apfel with `apfel --serve --cors`.
|
|
29
23
|
|
|
30
|
-
##
|
|
24
|
+
## Images
|
|
31
25
|
|
|
32
|
-
|
|
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.
|
|
33
27
|
|
|
34
|
-
|
|
35
|
-
2. The arguments are written again, constrained to the schema, with those fields required. Fields the model already named and typed right are kept as it wrote them.
|
|
28
|
+
## Config
|
|
36
29
|
|
|
37
|
-
|
|
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.
|
|
38
32
|
|
|
39
|
-
##
|
|
33
|
+
## Availability
|
|
40
34
|
|
|
41
|
-
|
|
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.
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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`
|
|
47
48
|
|
|
48
|
-
**Status:** early (0.
|
|
49
|
+
**Status:** early (0.x). The API may change.
|
package/dist/adapter.d.ts
CHANGED
|
@@ -1,98 +1,45 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
export declare const APFEL_BASE_URL = "http://127.0.0.1:11434/v1";
|
|
6
|
-
/** The models apfel serves: Apple's on-device Foundation Model. */
|
|
7
|
-
export declare const APFEL_TEXT_MODELS: readonly ["apple-foundationmodel"];
|
|
8
|
-
/** A model apfel serves. */
|
|
9
|
-
export type ApfelTextModel = (typeof APFEL_TEXT_MODELS)[number] | (string & {});
|
|
10
|
-
export type ApfelTextConfig = {
|
|
11
|
-
/** apfel's OpenAI-compatible API, up to `/v1`. `APFEL_BASE_URL` by default. */
|
|
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. */
|
|
12
5
|
baseURL?: string;
|
|
13
6
|
/** Headers sent with every request, such as apfel's bearer token. */
|
|
14
7
|
defaultHeaders?: Record<string, string>;
|
|
15
8
|
};
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* arrives; apfel itself reports token counts only.
|
|
20
|
-
*/
|
|
21
|
-
export type ApfelProviderUsageDetails = {
|
|
22
|
-
/** From sending the request to the first generated text. */
|
|
23
|
-
firstTokenMs?: number;
|
|
24
|
-
/** From the first generated text to the last. */
|
|
25
|
-
decodeMs?: number;
|
|
26
|
-
/**
|
|
27
|
-
* Tokens generated after the first chunk, estimated: apfel streams a few
|
|
28
|
-
* tokens per chunk, and the first chunk's share of the characters stands
|
|
29
|
-
* in for its share of the tokens.
|
|
30
|
-
*/
|
|
31
|
-
decodeTokens?: number;
|
|
32
|
-
/**
|
|
33
|
-
* Time spent rewriting the tool calls' arguments under their schemas,
|
|
34
|
-
* after the reply ended (see the README's "Tool calls").
|
|
35
|
-
*/
|
|
36
|
-
toolRepairMs?: number;
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* Request fields apfel takes for Apple's model. apfel rejects parameters
|
|
40
|
-
* the model doesn't support (stop, penalties, n…) with HTTP 400.
|
|
41
|
-
*/
|
|
42
|
-
export type ApfelModelOptions = {
|
|
43
|
-
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. */
|
|
44
12
|
max_tokens?: number;
|
|
13
|
+
temperature?: number;
|
|
14
|
+
top_p?: number;
|
|
45
15
|
seed?: number;
|
|
46
16
|
};
|
|
47
|
-
|
|
48
|
-
type
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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>;
|
|
54
29
|
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
readonly name: "apfel";
|
|
58
|
-
readonly provider: "apfel";
|
|
59
|
-
readonly api: "openai-completions";
|
|
60
|
-
readonly inputModalities: readonly ["text"];
|
|
61
|
-
private readonly baseURL;
|
|
62
|
-
private readonly headers;
|
|
63
|
-
constructor(config: ApfelTextConfig, model: TModel);
|
|
64
|
-
chatStream(options: TextOptions<ApfelModelOptions>): AsyncIterable<AdapterYieldChunk>;
|
|
65
|
-
/**
|
|
66
|
-
* A tool call's arguments, written again under the tool's schema with
|
|
67
|
-
* guided generation. Undefined if the tool takes no arguments or a request
|
|
68
|
-
* failed: the model's own arguments are used then.
|
|
69
|
-
*/
|
|
70
|
-
private repairArguments;
|
|
71
|
-
/**
|
|
72
|
-
* A JSON answer for `chat({ outputSchema })`, from apfel's JSON mode:
|
|
73
|
-
* `response_format: json_schema` constrains Apple's model to the schema
|
|
74
|
-
* (guided generation). `chat()` validates the result.
|
|
75
|
-
*/
|
|
76
|
-
structuredOutput(options: StructuredOutputOptions<ApfelModelOptions>): Promise<StructuredOutputResult<unknown>>;
|
|
77
|
-
}
|
|
30
|
+
/** A model apfel serves: Apple's ("apple-foundationmodel", the default), or any other name. */
|
|
31
|
+
export type ApfelModel = ApfelChatModel | (string & {});
|
|
78
32
|
/**
|
|
79
|
-
*
|
|
80
|
-
* by apfel.
|
|
33
|
+
* An adapter for a model apfel serves: Apple's on-device model by default.
|
|
81
34
|
*
|
|
82
35
|
* @example
|
|
83
36
|
* ```ts
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* messages,
|
|
87
|
-
*
|
|
88
|
-
* })
|
|
37
|
+
* const adapter = apfelText()
|
|
38
|
+
* if ((await adapter.availability()) === "available") {
|
|
39
|
+
* chat({ adapter, messages, tools })
|
|
40
|
+
* }
|
|
89
41
|
* ```
|
|
90
42
|
*/
|
|
91
|
-
export declare function apfelText
|
|
92
|
-
|
|
93
|
-
* Whether apfel is running and serves the model: true if `GET /v1/models`
|
|
94
|
-
* lists it within 10 seconds.
|
|
95
|
-
*/
|
|
96
|
-
export declare function isApfelAvailable(model: ApfelTextModel, config?: ApfelTextConfig): Promise<boolean>;
|
|
97
|
-
export {};
|
|
43
|
+
export declare function apfelText(config?: ApfelConfig): ApfelTextAdapter;
|
|
44
|
+
export declare function apfelText(model: ApfelModel, config?: ApfelConfig): ApfelTextAdapter;
|
|
98
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;AAIjF,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"}
|