@tanstack-ai-browser/afm 0.2.2 → 0.2.3
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 +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +2 -0
- package/package.json +1 -1
- package/src/adapter.test.ts +12 -0
- package/src/adapter.ts +2 -0
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ const adapter = afmText({ guardrails: "permissiveContentTransformations" })
|
|
|
43
43
|
|
|
44
44
|
A request's `modelOptions.guardrails` overrides the adapter's, and in Electron `serveAfm({ guardrails })` sets them for every page that names none. `"default"` is the default.
|
|
45
45
|
|
|
46
|
-
Apple's permissive guardrails cover text responses only: anything else, such as structured output (`chat({ outputSchema })`), gets the default guardrails. So when a permissive session's structured answer is blocked, the adapter asks for it again as text: a JSON value to the schema. The framework doesn't hold that answer to the schema while it's written, so the adapter parses the whole answer (or everything inside a code fence) and checks it against the schema before returning it; one that doesn't fit fails with the code `schemaMismatch`, and one that isn't JSON with `invalidJson`. (`chat()` itself validates only schemas that come with a Standard Schema validator, not plain JSON Schema.) Tool calls' arguments keep the default guardrails. A guardrail violation that still happens
|
|
46
|
+
Apple's permissive guardrails cover text responses only: anything else, such as structured output (`chat({ outputSchema })`), gets the default guardrails. So when a permissive session's structured answer is blocked, the adapter asks for it again as text: a JSON value to the schema. The framework doesn't hold that answer to the schema while it's written, so the adapter parses the whole answer (or everything inside a code fence) and checks it against the schema before returning it; one that doesn't fit fails with the code `schemaMismatch`, and one that isn't JSON with `invalidJson`. (`chat()` itself validates only schemas that come with a Standard Schema validator, not plain JSON Schema.) Tool calls' arguments keep the default guardrails. A guardrail violation that still happens has the code `guardrailViolation`: on the error `chat()` throws, and on a stream's `RUN_ERROR`. Apple words it "May contain unsafe content" or "Response may contain sensitive or unsafe content"; it's one check either way. With structured output (`chat({ outputSchema })`), TanStack AI 1.4 doesn't carry an adapter's error code through, so there only the message tells.
|
|
47
47
|
|
|
48
48
|
### Electron (macOS)
|
|
49
49
|
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,iBAAiB,EAAgB,WAAW,EAAoB,MAAM,cAAc,CAAA;AAClG,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAG5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAA8D,MAAM,aAAa,CAAA;AAKvH,MAAM,MAAM,SAAS,GAAG;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,aAAa,CAAA;CAC3B,CAAA;AAED,uFAAuF;AACvF,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+BAA+B;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,sFAAsF;IACtF,UAAU,CAAC,EAAE,aAAa,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,WAAW,CAAA;AAEvF,KAAK,kBAAkB,GAAG;IACxB,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA;AAgJD,qBAAa,cAAe,SAAQ,eAAe,CACjD,YAAY,EACZ,mBAAmB,EACnB,SAAS,CAAC,MAAM,CAAC,EACjB,kBAAkB,CACnB;IACC,SAAkB,IAAI,EAAG,MAAM,CAAS;IACxC,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAS;IAC9B,SAAkB,QAAQ,EAAG,KAAK,CAAS;IAC3C,SAAkB,GAAG,EAAG,KAAK,CAAS;IACtC,SAAkB,eAAe,YAAI,MAAM,EAAU;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,SAAS,CAA8B;IAE/C,YAAY,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAIjD;IAED,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;IAEtD,wGAAwG;IACxG,OAAO,CAAC,YAAY;IAIpB,0FAA0F;IACpF,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC,CAO1C;IAED,kEAAkE;IAC5D,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAM/C;IAED,wEAAwE;IAClE,MAAM,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAMlE;IAED;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAK7B;IAED,yEAAyE;IACzE,OAAO,CAAC,MAAM;IAQd,6EAA6E;IAC7E,OAAO,CAAC,KAAK;IAOb,+EAA+E;IAC/E,OAAO,CAAC,IAAI;IAWZ,6DAA6D;YAC/C,IAAI;IA+BlB;;;;OAIG;YACW,KAAK;IAsDZ,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,mBAAmB,CAAC,GAAG,aAAa,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter.js
CHANGED
|
@@ -459,6 +459,8 @@ export class AfmTextAdapter extends BaseTextAdapter {
|
|
|
459
459
|
model,
|
|
460
460
|
timestamp: timestamp(),
|
|
461
461
|
message,
|
|
462
|
+
// At the top too: chat() puts only this one on the error it throws.
|
|
463
|
+
...(typeof code === "string" && { code }),
|
|
462
464
|
error: { message, ...(typeof code === "string" && { code }) }
|
|
463
465
|
};
|
|
464
466
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack-ai-browser/afm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "TanStack AI adapter for Apple's on-device Foundation Model (AFM), through Apple's framework itself, with native tool calling: a Capacitor plugin for iOS, and serveAfm() for Electron on macOS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "daveycodez",
|
package/src/adapter.test.ts
CHANGED
|
@@ -426,3 +426,15 @@ test("a structured answer written as text (asText) must fit the schema: chat() a
|
|
|
426
426
|
await assert.rejects(ask('{"city":"Paris","unit":"kelvin"}'), /\/unit: "kelvin" is not one of "c", "f"/)
|
|
427
427
|
await assert.rejects(ask("Sorry, I can't help with that."), /wrote no JSON for the schema/)
|
|
428
428
|
})
|
|
429
|
+
|
|
430
|
+
test("a run's error keeps its code through chat(): on the thrown error, and on the stream's RUN_ERROR", async () => {
|
|
431
|
+
native.replies = [[{ type: "error", code: "guardrailViolation", message: "May contain unsafe content" }]]
|
|
432
|
+
const thrown = (await (chat({ adapter: afmText({ native }), stream: false, threadId: "errors", messages: [user("Searing Blood")] } as never) as unknown as Promise<unknown>).catch((e: unknown) => e)) as Error & { code?: string }
|
|
433
|
+
assert.equal(thrown.message, "May contain unsafe content")
|
|
434
|
+
assert.equal(thrown.code, "guardrailViolation")
|
|
435
|
+
native.replies = [[{ type: "error", code: "guardrailViolation", message: "May contain unsafe content" }]]
|
|
436
|
+
const chunks = await run(afmText({ native }), { threadId: "errors-stream" })
|
|
437
|
+
const error = chunks.find((c) => c.type === "RUN_ERROR")
|
|
438
|
+
assert.equal(error?.code, "guardrailViolation")
|
|
439
|
+
assert.deepEqual(error?.error, { message: "May contain unsafe content", code: "guardrailViolation" })
|
|
440
|
+
})
|
package/src/adapter.ts
CHANGED
|
@@ -535,6 +535,8 @@ export class AfmTextAdapter extends BaseTextAdapter<
|
|
|
535
535
|
model,
|
|
536
536
|
timestamp: timestamp(),
|
|
537
537
|
message,
|
|
538
|
+
// At the top too: chat() puts only this one on the error it throws.
|
|
539
|
+
...(typeof code === "string" && { code }),
|
|
538
540
|
error: { message, ...(typeof code === "string" && { code }) }
|
|
539
541
|
}
|
|
540
542
|
} finally {
|