@kernl-sdk/ai 0.2.10 → 0.3.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @kernl-sdk/ai@0.2.9 build /Users/andjones/Documents/projects/kernl/packages/providers/ai
2
+ > @kernl-sdk/ai@0.3.1 build /home/runner/work/kernl/kernl/packages/providers/ai
3
3
  > tsc && tsc-alias --resolve-full-paths
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @kernl/ai
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [572ae80]
8
+ - @kernl-sdk/protocol@0.3.0
9
+ - @kernl-sdk/retrieval@0.1.4
10
+
11
+ ## 0.3.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 3fe8682: Add native structured output support for agents
16
+
17
+ **kernl**
18
+ - Add `output` field to Agent config (Zod schema for structured responses)
19
+ - Rename type params: `TResponse` → `TOutput`, `AgentResponseType` → `AgentOutputType`
20
+ - Wire `agent.output` through Thread to protocol's `responseType`
21
+
22
+ **@kernl-sdk/ai**
23
+ - Add `RESPONSE_FORMAT` codec for AI SDK's `responseFormat` parameter
24
+ - Add structured output integration tests for OpenAI, Anthropic, and Google
25
+
3
26
  ## 0.2.10
4
27
 
5
28
  ### Patch Changes
@@ -1,6 +1,7 @@
1
1
  import { describe, it, expect, beforeAll } from "vitest";
2
2
  import { openai } from "@ai-sdk/openai";
3
3
  import { anthropic } from "@ai-sdk/anthropic";
4
+ import { google } from "@ai-sdk/google";
4
5
  import { IN_PROGRESS } from "@kernl-sdk/protocol";
5
6
  import { AISDKLanguageModel } from "../language-model.js";
6
7
  /**
@@ -9,11 +10,26 @@ import { AISDKLanguageModel } from "../language-model.js";
9
10
  * These tests require API keys to be set:
10
11
  * - OPENAI_API_KEY for OpenAI tests
11
12
  * - ANTHROPIC_API_KEY for Anthropic tests
13
+ * - GOOGLE_GENERATIVE_AI_API_KEY for Google tests
12
14
  *
13
- * Run with: OPENAI_API_KEY=your-key ANTHROPIC_API_KEY=your-key pnpm test:run
15
+ * Run with: OPENAI_API_KEY=your-key ANTHROPIC_API_KEY=your-key GOOGLE_GENERATIVE_AI_API_KEY=your-key pnpm test:run
14
16
  */
15
17
  const SKIP_OPENAI_TESTS = !process.env.OPENAI_API_KEY;
16
18
  const SKIP_ANTHROPIC_TESTS = !process.env.ANTHROPIC_API_KEY;
19
+ const SKIP_GOOGLE_TESTS = !process.env.GOOGLE_GENERATIVE_AI_API_KEY;
20
+ /**
21
+ * Shared JSON schema for structured output tests.
22
+ * Extracts a person's name and age from text.
23
+ */
24
+ const PERSON_SCHEMA = {
25
+ type: "object",
26
+ properties: {
27
+ name: { type: "string", description: "The person's name" },
28
+ age: { type: "number", description: "The person's age in years" },
29
+ },
30
+ required: ["name", "age"],
31
+ additionalProperties: false,
32
+ };
17
33
  describe.skipIf(SKIP_OPENAI_TESTS)("AISDKLanguageModel - OpenAI", () => {
18
34
  let gpt41;
19
35
  beforeAll(() => {
@@ -645,6 +661,94 @@ describe.skipIf(SKIP_OPENAI_TESTS)("AISDKLanguageModel - OpenAI", () => {
645
661
  })).rejects.toThrow(/max_output_tokens/);
646
662
  });
647
663
  });
664
+ describe("structured output", () => {
665
+ it("should generate structured JSON output with responseType", async () => {
666
+ const response = await gpt41.generate({
667
+ input: [
668
+ {
669
+ kind: "message",
670
+ role: "user",
671
+ id: "msg-1",
672
+ content: [
673
+ {
674
+ kind: "text",
675
+ text: "Extract the person info: John Smith is 42 years old.",
676
+ },
677
+ ],
678
+ },
679
+ ],
680
+ responseType: {
681
+ kind: "json",
682
+ schema: PERSON_SCHEMA,
683
+ name: "person",
684
+ description: "A person with name and age",
685
+ },
686
+ settings: {
687
+ maxTokens: 100,
688
+ temperature: 0,
689
+ },
690
+ });
691
+ expect(response.content).toBeDefined();
692
+ expect(response.content.length).toBeGreaterThan(0);
693
+ // Find the assistant message with JSON output
694
+ const messages = response.content.filter((item) => item.kind === "message" && item.role === "assistant");
695
+ expect(messages.length).toBeGreaterThan(0);
696
+ const msg = messages[0];
697
+ const textContent = msg.content.find((c) => c.kind === "text");
698
+ expect(textContent).toBeDefined();
699
+ // Parse and validate the JSON output
700
+ const parsed = JSON.parse(textContent.text);
701
+ expect(parsed.name).toBe("John Smith");
702
+ expect(parsed.age).toBe(42);
703
+ });
704
+ it("should stream structured JSON output with responseType", async () => {
705
+ const events = [];
706
+ for await (const event of gpt41.stream({
707
+ input: [
708
+ {
709
+ kind: "message",
710
+ role: "user",
711
+ id: "msg-1",
712
+ content: [
713
+ {
714
+ kind: "text",
715
+ text: "Extract the person info: Alice Wong is 28 years old.",
716
+ },
717
+ ],
718
+ },
719
+ ],
720
+ responseType: {
721
+ kind: "json",
722
+ schema: PERSON_SCHEMA,
723
+ name: "person",
724
+ description: "A person with name and age",
725
+ },
726
+ settings: {
727
+ maxTokens: 100,
728
+ temperature: 0,
729
+ },
730
+ })) {
731
+ events.push(event);
732
+ }
733
+ expect(events.length).toBeGreaterThan(0);
734
+ // Should have text-delta events for streaming JSON
735
+ const textDeltas = events.filter((e) => e.kind === "text-delta");
736
+ expect(textDeltas.length).toBeGreaterThan(0);
737
+ // Should have a complete message with the JSON
738
+ const messages = events.filter((e) => e.kind === "message");
739
+ expect(messages.length).toBeGreaterThan(0);
740
+ const msg = messages[0];
741
+ const textContent = msg.content.find((c) => c.kind === "text");
742
+ expect(textContent).toBeDefined();
743
+ // Parse and validate the JSON output
744
+ const parsed = JSON.parse(textContent.text);
745
+ expect(parsed.name).toBe("Alice Wong");
746
+ expect(parsed.age).toBe(28);
747
+ // Should have finish event
748
+ const finishEvents = events.filter((e) => e.kind === "finish");
749
+ expect(finishEvents.length).toBe(1);
750
+ });
751
+ });
648
752
  });
649
753
  describe.skipIf(SKIP_ANTHROPIC_TESTS)("AISDKLanguageModel - Anthropic", () => {
650
754
  let claude;
@@ -773,4 +877,241 @@ describe.skipIf(SKIP_ANTHROPIC_TESTS)("AISDKLanguageModel - Anthropic", () => {
773
877
  expect(finishEvents.length).toBe(1);
774
878
  });
775
879
  });
880
+ describe("structured output", () => {
881
+ it("should generate structured JSON output with responseType", async () => {
882
+ const response = await claude.generate({
883
+ input: [
884
+ {
885
+ kind: "message",
886
+ role: "user",
887
+ id: "msg-1",
888
+ content: [
889
+ {
890
+ kind: "text",
891
+ text: "Extract the person info: Maria Garcia is 35 years old.",
892
+ },
893
+ ],
894
+ },
895
+ ],
896
+ responseType: {
897
+ kind: "json",
898
+ schema: PERSON_SCHEMA,
899
+ name: "person",
900
+ description: "A person with name and age",
901
+ },
902
+ settings: {
903
+ maxTokens: 100,
904
+ temperature: 0,
905
+ },
906
+ });
907
+ expect(response.content).toBeDefined();
908
+ expect(response.content.length).toBeGreaterThan(0);
909
+ // Find the assistant message with JSON output
910
+ const messages = response.content.filter((item) => item.kind === "message" && item.role === "assistant");
911
+ expect(messages.length).toBeGreaterThan(0);
912
+ const msg = messages[0];
913
+ const textContent = msg.content.find((c) => c.kind === "text");
914
+ expect(textContent).toBeDefined();
915
+ // Parse and validate the JSON output
916
+ const parsed = JSON.parse(textContent.text);
917
+ expect(parsed.name).toBe("Maria Garcia");
918
+ expect(parsed.age).toBe(35);
919
+ });
920
+ it("should stream structured JSON output with responseType", async () => {
921
+ const events = [];
922
+ for await (const event of claude.stream({
923
+ input: [
924
+ {
925
+ kind: "message",
926
+ role: "user",
927
+ id: "msg-1",
928
+ content: [
929
+ {
930
+ kind: "text",
931
+ text: "Extract the person info: David Chen is 55 years old.",
932
+ },
933
+ ],
934
+ },
935
+ ],
936
+ responseType: {
937
+ kind: "json",
938
+ schema: PERSON_SCHEMA,
939
+ name: "person",
940
+ description: "A person with name and age",
941
+ },
942
+ settings: {
943
+ maxTokens: 100,
944
+ temperature: 0,
945
+ },
946
+ })) {
947
+ events.push(event);
948
+ }
949
+ expect(events.length).toBeGreaterThan(0);
950
+ // Should have text-delta events for streaming JSON
951
+ const textDeltas = events.filter((e) => e.kind === "text-delta");
952
+ expect(textDeltas.length).toBeGreaterThan(0);
953
+ // Should have a complete message with the JSON
954
+ const messages = events.filter((e) => e.kind === "message");
955
+ expect(messages.length).toBeGreaterThan(0);
956
+ const msg = messages[0];
957
+ const textContent = msg.content.find((c) => c.kind === "text");
958
+ expect(textContent).toBeDefined();
959
+ // Parse and validate the JSON output
960
+ const parsed = JSON.parse(textContent.text);
961
+ expect(parsed.name).toBe("David Chen");
962
+ expect(parsed.age).toBe(55);
963
+ // Should have finish event
964
+ const finishEvents = events.filter((e) => e.kind === "finish");
965
+ expect(finishEvents.length).toBe(1);
966
+ });
967
+ });
968
+ });
969
+ describe.skipIf(SKIP_GOOGLE_TESTS)("AISDKLanguageModel - Google", () => {
970
+ let gemini;
971
+ beforeAll(() => {
972
+ gemini = new AISDKLanguageModel(google("gemini-2.5-flash-lite"));
973
+ });
974
+ describe("generate", () => {
975
+ it("should generate a simple text response", async () => {
976
+ const response = await gemini.generate({
977
+ input: [
978
+ {
979
+ kind: "message",
980
+ role: "user",
981
+ id: "msg-1",
982
+ content: [
983
+ { kind: "text", text: "Say 'Hello, World!' and nothing else." },
984
+ ],
985
+ },
986
+ ],
987
+ settings: {
988
+ maxTokens: 50,
989
+ temperature: 0,
990
+ },
991
+ });
992
+ expect(response.content).toBeDefined();
993
+ expect(response.content.length).toBeGreaterThan(0);
994
+ expect(response.usage).toBeDefined();
995
+ expect(response.usage.totalTokens).toBeGreaterThan(0);
996
+ const messages = response.content.filter((item) => item.kind === "message");
997
+ expect(messages.length).toBeGreaterThan(0);
998
+ });
999
+ });
1000
+ describe("stream", () => {
1001
+ it("should stream text responses", async () => {
1002
+ const events = [];
1003
+ for await (const event of gemini.stream({
1004
+ input: [
1005
+ {
1006
+ kind: "message",
1007
+ role: "user",
1008
+ id: "msg-1",
1009
+ content: [{ kind: "text", text: "Count to 5" }],
1010
+ },
1011
+ ],
1012
+ settings: {
1013
+ maxTokens: 50,
1014
+ temperature: 0,
1015
+ },
1016
+ })) {
1017
+ events.push(event);
1018
+ }
1019
+ expect(events.length).toBeGreaterThan(0);
1020
+ // Should have at least one finish event
1021
+ const finishEvents = events.filter((e) => e.kind === "finish");
1022
+ expect(finishEvents.length).toBe(1);
1023
+ // Should have usage information
1024
+ const finishEvent = finishEvents[0];
1025
+ expect(finishEvent.usage).toBeDefined();
1026
+ expect(finishEvent.usage.totalTokens).toBeGreaterThan(0);
1027
+ });
1028
+ });
1029
+ describe("structured output", () => {
1030
+ it("should generate structured JSON output with responseType", async () => {
1031
+ const response = await gemini.generate({
1032
+ input: [
1033
+ {
1034
+ kind: "message",
1035
+ role: "user",
1036
+ id: "msg-1",
1037
+ content: [
1038
+ {
1039
+ kind: "text",
1040
+ text: "Extract the person info: Kenji Tanaka is 29 years old.",
1041
+ },
1042
+ ],
1043
+ },
1044
+ ],
1045
+ responseType: {
1046
+ kind: "json",
1047
+ schema: PERSON_SCHEMA,
1048
+ name: "person",
1049
+ description: "A person with name and age",
1050
+ },
1051
+ settings: {
1052
+ maxTokens: 100,
1053
+ temperature: 0,
1054
+ },
1055
+ });
1056
+ expect(response.content).toBeDefined();
1057
+ expect(response.content.length).toBeGreaterThan(0);
1058
+ // Find the assistant message with JSON output
1059
+ const messages = response.content.filter((item) => item.kind === "message" && item.role === "assistant");
1060
+ expect(messages.length).toBeGreaterThan(0);
1061
+ const msg = messages[0];
1062
+ const textContent = msg.content.find((c) => c.kind === "text");
1063
+ expect(textContent).toBeDefined();
1064
+ // Parse and validate the JSON output
1065
+ const parsed = JSON.parse(textContent.text);
1066
+ expect(parsed.name).toBe("Kenji Tanaka");
1067
+ expect(parsed.age).toBe(29);
1068
+ });
1069
+ it("should stream structured JSON output with responseType", async () => {
1070
+ const events = [];
1071
+ for await (const event of gemini.stream({
1072
+ input: [
1073
+ {
1074
+ kind: "message",
1075
+ role: "user",
1076
+ id: "msg-1",
1077
+ content: [
1078
+ {
1079
+ kind: "text",
1080
+ text: "Extract the person info: Sarah Johnson is 41 years old.",
1081
+ },
1082
+ ],
1083
+ },
1084
+ ],
1085
+ responseType: {
1086
+ kind: "json",
1087
+ schema: PERSON_SCHEMA,
1088
+ name: "person",
1089
+ description: "A person with name and age",
1090
+ },
1091
+ settings: {
1092
+ maxTokens: 100,
1093
+ temperature: 0,
1094
+ },
1095
+ })) {
1096
+ events.push(event);
1097
+ }
1098
+ expect(events.length).toBeGreaterThan(0);
1099
+ // Should have text-delta events for streaming JSON
1100
+ const textDeltas = events.filter((e) => e.kind === "text-delta");
1101
+ expect(textDeltas.length).toBeGreaterThan(0);
1102
+ // Should have a complete message with the JSON
1103
+ const messages = events.filter((e) => e.kind === "message");
1104
+ expect(messages.length).toBeGreaterThan(0);
1105
+ const msg = messages[0];
1106
+ const textContent = msg.content.find((c) => c.kind === "text");
1107
+ expect(textContent).toBeDefined();
1108
+ // Parse and validate the JSON output
1109
+ const parsed = JSON.parse(textContent.text);
1110
+ expect(parsed.name).toBe("Sarah Johnson");
1111
+ expect(parsed.age).toBe(41);
1112
+ // Should have finish event
1113
+ const finishEvents = events.filter((e) => e.kind === "finish");
1114
+ expect(finishEvents.length).toBe(1);
1115
+ });
1116
+ });
776
1117
  });
@@ -1,6 +1,6 @@
1
1
  import type { Codec } from "@kernl-sdk/shared/lib";
2
- import { type LanguageModelResponse, type LanguageModelWarning } from "@kernl-sdk/protocol";
3
- import type { LanguageModelV3Content, LanguageModelV3FinishReason, LanguageModelV3Usage, LanguageModelV3CallWarning } from "@ai-sdk/provider";
2
+ import { type LanguageModelResponse, type LanguageModelResponseType, type LanguageModelWarning } from "@kernl-sdk/protocol";
3
+ import type { LanguageModelV3Content, LanguageModelV3FinishReason, LanguageModelV3Usage, LanguageModelV3CallWarning, JSONSchema7 } from "@ai-sdk/provider";
4
4
  /**
5
5
  * AI SDK generate result structure
6
6
  */
@@ -13,4 +13,22 @@ export interface AISdkGenerateResult {
13
13
  }
14
14
  export declare const MODEL_RESPONSE: Codec<LanguageModelResponse, AISdkGenerateResult>;
15
15
  export declare const WARNING: Codec<LanguageModelWarning, LanguageModelV3CallWarning>;
16
+ /**
17
+ * AI SDK response format type.
18
+ *
19
+ * Maps to the `responseFormat` parameter in AI SDK's doGenerate/doStream.
20
+ */
21
+ export interface AISdkResponseFormat {
22
+ type: "json";
23
+ schema?: JSONSchema7;
24
+ name?: string;
25
+ description?: string;
26
+ }
27
+ /**
28
+ * Codec for converting protocol responseType to AI SDK responseFormat.
29
+ *
30
+ * - `kind: "text"` or undefined → undefined (AI SDK defaults to text)
31
+ * - `kind: "json"` → `{ type: "json", schema, name, description }`
32
+ */
33
+ export declare const RESPONSE_FORMAT: Codec<LanguageModelResponseType | undefined, AISdkResponseFormat | undefined>;
16
34
  //# sourceMappingURL=response.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/convert/response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAIL,KAAK,qBAAqB,EAI1B,KAAK,oBAAoB,EAE1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,oBAAoB,EACpB,0BAA0B,EAC3B,MAAM,kBAAkB,CAAC;AAE1B;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACvC,YAAY,EAAE,2BAA2B,CAAC;IAC1C,KAAK,EAAE,oBAAoB,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,QAAQ,EAAE,KAAK,CAAC,0BAA0B,CAAC,CAAC;CAC7C;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,qBAAqB,EAAE,mBAAmB,CAiG1E,CAAC;AAmBJ,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,oBAAoB,EAAE,0BAA0B,CA0BzE,CAAC"}
1
+ {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/convert/response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAIL,KAAK,qBAAqB,EAE1B,KAAK,yBAAyB,EAG9B,KAAK,oBAAoB,EAE1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,oBAAoB,EACpB,0BAA0B,EAC1B,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAE1B;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACvC,YAAY,EAAE,2BAA2B,CAAC;IAC1C,KAAK,EAAE,oBAAoB,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,QAAQ,EAAE,KAAK,CAAC,0BAA0B,CAAC,CAAC;CAC7C;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,qBAAqB,EAAE,mBAAmB,CAiG1E,CAAC;AAmBJ,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,oBAAoB,EAAE,0BAA0B,CA0BzE,CAAC;AAEJ;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CACjC,yBAAyB,GAAG,SAAS,EACrC,mBAAmB,GAAG,SAAS,CAiBhC,CAAC"}
@@ -123,3 +123,25 @@ export const WARNING = {
123
123
  }
124
124
  },
125
125
  };
126
+ /**
127
+ * Codec for converting protocol responseType to AI SDK responseFormat.
128
+ *
129
+ * - `kind: "text"` or undefined → undefined (AI SDK defaults to text)
130
+ * - `kind: "json"` → `{ type: "json", schema, name, description }`
131
+ */
132
+ export const RESPONSE_FORMAT = {
133
+ encode: (responseType) => {
134
+ if (!responseType || responseType.kind === "text") {
135
+ return undefined;
136
+ }
137
+ return {
138
+ type: "json",
139
+ schema: responseType.schema,
140
+ name: responseType.name,
141
+ description: responseType.description,
142
+ };
143
+ },
144
+ decode: () => {
145
+ throw new Error("codec:unimplemented");
146
+ },
147
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"language-model.d.ts","sourceRoot":"","sources":["../src/language-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAS7B;;GAEG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IAK1C,OAAO,CAAC,KAAK;IAJzB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEL,KAAK,EAAE,eAAe;IAK1C;;OAEG;IACG,QAAQ,CACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAejC;;OAEG;IACI,MAAM,CACX,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,wBAAwB,CAAC;CAgF3C"}
1
+ {"version":3,"file":"language-model.d.ts","sourceRoot":"","sources":["../src/language-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAS7B;;GAEG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IAK1C,OAAO,CAAC,KAAK;IAJzB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEL,KAAK,EAAE,eAAe;IAK1C;;OAEG;IACG,QAAQ,CACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAiBjC;;OAEG;IACI,MAAM,CACX,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,wBAAwB,CAAC;CAkF3C"}
@@ -2,7 +2,7 @@ import { message, reasoning } from "@kernl-sdk/protocol";
2
2
  import { MESSAGE } from "./convert/message.js";
3
3
  import { TOOL } from "./convert/tools.js";
4
4
  import { MODEL_SETTINGS } from "./convert/settings.js";
5
- import { MODEL_RESPONSE } from "./convert/response.js";
5
+ import { MODEL_RESPONSE, RESPONSE_FORMAT } from "./convert/response.js";
6
6
  import { convertStream } from "./convert/stream.js";
7
7
  /**
8
8
  * LanguageModel adapter for the AI SDK LanguageModelV3.
@@ -24,10 +24,12 @@ export class AISDKLanguageModel {
24
24
  const messages = request.input.map(MESSAGE.encode);
25
25
  const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
26
26
  const settings = MODEL_SETTINGS.encode(request.settings);
27
+ const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
27
28
  const result = await this.model.doGenerate({
28
29
  prompt: messages,
29
30
  tools,
30
31
  ...settings,
32
+ responseFormat,
31
33
  abortSignal: request.abort,
32
34
  });
33
35
  return MODEL_RESPONSE.decode(result);
@@ -39,10 +41,12 @@ export class AISDKLanguageModel {
39
41
  const messages = request.input.map(MESSAGE.encode);
40
42
  const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
41
43
  const settings = MODEL_SETTINGS.encode(request.settings);
44
+ const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
42
45
  const stream = await this.model.doStream({
43
46
  prompt: messages,
44
47
  tools,
45
48
  ...settings,
49
+ responseFormat,
46
50
  abortSignal: request.abort,
47
51
  });
48
52
  // text + reasoning buffers for delta accumulation
@@ -1,4 +1,4 @@
1
- import { anthropic as createAnthropicModel } from "@ai-sdk/anthropic";
1
+ import { anthropic as _anthropic } from "@ai-sdk/anthropic";
2
2
  import { AISDKLanguageModel } from "../language-model.js";
3
3
  /**
4
4
  * Create a kernl-compatible Anthropic language model.
@@ -12,7 +12,7 @@ import { AISDKLanguageModel } from "../language-model.js";
12
12
  * ```
13
13
  */
14
14
  export function anthropic(modelId) {
15
- const model = createAnthropicModel(modelId);
15
+ const model = _anthropic(modelId);
16
16
  return new AISDKLanguageModel(model);
17
17
  }
18
18
  // Note: Anthropic does not currently support embeddings
@@ -1,4 +1,4 @@
1
- import { google as createGoogleModel } from "@ai-sdk/google";
1
+ import { google as _google } from "@ai-sdk/google";
2
2
  import { AISDKLanguageModel } from "../language-model.js";
3
3
  import { AISDKEmbeddingModel } from "../embedding-model.js";
4
4
  import { registerEmbeddingProvider } from "@kernl-sdk/retrieval";
@@ -14,8 +14,8 @@ import { registerEmbeddingProvider } from "@kernl-sdk/retrieval";
14
14
  * ```
15
15
  */
16
16
  export function google(modelId) {
17
- const model = createGoogleModel(modelId);
17
+ const model = _google(modelId);
18
18
  return new AISDKLanguageModel(model);
19
19
  }
20
20
  // Auto-register Google embedding provider
21
- registerEmbeddingProvider("google", (id) => new AISDKEmbeddingModel(createGoogleModel.textEmbedding(id)));
21
+ registerEmbeddingProvider("google", (id) => new AISDKEmbeddingModel(_google.textEmbedding(id)));
@@ -1,4 +1,4 @@
1
- import { openai as createOpenAIModel } from "@ai-sdk/openai";
1
+ import { openai as _openai } from "@ai-sdk/openai";
2
2
  import { AISDKLanguageModel } from "../language-model.js";
3
3
  import { AISDKEmbeddingModel } from "../embedding-model.js";
4
4
  import { registerEmbeddingProvider } from "@kernl-sdk/retrieval";
@@ -14,8 +14,8 @@ import { registerEmbeddingProvider } from "@kernl-sdk/retrieval";
14
14
  * ```
15
15
  */
16
16
  export function openai(modelId) {
17
- const model = createOpenAIModel(modelId);
17
+ const model = _openai(modelId);
18
18
  return new AISDKLanguageModel(model);
19
19
  }
20
20
  // Auto-register OpenAI embedding provider
21
- registerEmbeddingProvider("openai", (id) => new AISDKEmbeddingModel(createOpenAIModel.embedding(id)));
21
+ registerEmbeddingProvider("openai", (id) => new AISDKEmbeddingModel(_openai.embedding(id)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kernl-sdk/ai",
3
- "version": "0.2.10",
3
+ "version": "0.3.1",
4
4
  "description": "Vercel AI SDK adapter for kernl",
5
5
  "keywords": [
6
6
  "kernl",
@@ -68,9 +68,9 @@
68
68
  "vitest": "^4.0.8"
69
69
  },
70
70
  "dependencies": {
71
- "@kernl-sdk/protocol": "0.2.8",
72
- "@kernl-sdk/shared": "^0.3.0",
73
- "@kernl-sdk/retrieval": "0.1.3"
71
+ "@kernl-sdk/protocol": "0.3.0",
72
+ "@kernl-sdk/retrieval": "0.1.4",
73
+ "@kernl-sdk/shared": "^0.3.0"
74
74
  },
75
75
  "scripts": {
76
76
  "build": "tsc && tsc-alias --resolve-full-paths",