@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +23 -0
- package/dist/__tests__/integration.test.js +342 -1
- package/dist/convert/response.d.ts +20 -2
- package/dist/convert/response.d.ts.map +1 -1
- package/dist/convert/response.js +22 -0
- package/dist/language-model.d.ts.map +1 -1
- package/dist/language-model.js +5 -1
- package/dist/providers/anthropic.js +2 -2
- package/dist/providers/google.js +3 -3
- package/dist/providers/openai.js +3 -3
- package/package.json +4 -4
- package/src/__tests__/integration.test.ts +400 -1
- package/src/convert/response.ts +41 -0
- package/src/language-model.ts +5 -1
- package/src/providers/anthropic.ts +2 -2
- package/src/providers/google.ts +5 -4
- package/src/providers/openai.ts +5 -4
- package/.turbo/turbo-check-types.log +0 -4
- package/dist/convert/messages.d.ts +0 -4
- package/dist/convert/messages.d.ts.map +0 -1
- package/dist/convert/messages.js +0 -130
- package/dist/error.d.ts +0 -8
- package/dist/error.d.ts.map +0 -1
- package/dist/error.js +0 -15
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
package/.turbo/turbo-build.log
CHANGED
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,
|
|
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"}
|
package/dist/convert/response.js
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/language-model.js
CHANGED
|
@@ -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
|
|
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 =
|
|
15
|
+
const model = _anthropic(modelId);
|
|
16
16
|
return new AISDKLanguageModel(model);
|
|
17
17
|
}
|
|
18
18
|
// Note: Anthropic does not currently support embeddings
|
package/dist/providers/google.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { google as
|
|
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 =
|
|
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(
|
|
21
|
+
registerEmbeddingProvider("google", (id) => new AISDKEmbeddingModel(_google.textEmbedding(id)));
|
package/dist/providers/openai.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { openai as
|
|
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 =
|
|
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(
|
|
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.
|
|
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.
|
|
72
|
-
"@kernl-sdk/
|
|
73
|
-
"@kernl-sdk/
|
|
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",
|