@yourgpt/llm-sdk 1.2.7 → 1.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/dist/index.js CHANGED
@@ -539,2291 +539,302 @@ var DEFAULT_CAPABILITIES = {
539
539
  supportedImageTypes: []
540
540
  };
541
541
 
542
- // src/providers/openai/provider.ts
543
- var OPENAI_MODELS = {
544
- // GPT-4o series
545
- "gpt-4o": { vision: true, tools: true, jsonMode: true, maxTokens: 128e3 },
546
- "gpt-4o-mini": {
547
- vision: true,
548
- tools: true,
549
- jsonMode: true,
550
- maxTokens: 128e3
551
- },
552
- "gpt-4o-2024-11-20": {
553
- vision: true,
554
- tools: true,
555
- jsonMode: true,
556
- maxTokens: 128e3
557
- },
558
- "gpt-4o-2024-08-06": {
559
- vision: true,
560
- tools: true,
561
- jsonMode: true,
562
- maxTokens: 128e3
563
- },
564
- // GPT-4 Turbo
565
- "gpt-4-turbo": {
566
- vision: true,
567
- tools: true,
568
- jsonMode: true,
569
- maxTokens: 128e3
570
- },
571
- "gpt-4-turbo-preview": {
572
- vision: false,
573
- tools: true,
574
- jsonMode: true,
575
- maxTokens: 128e3
576
- },
577
- // GPT-4
578
- "gpt-4": { vision: false, tools: true, jsonMode: false, maxTokens: 8192 },
579
- "gpt-4-32k": {
580
- vision: false,
581
- tools: true,
582
- jsonMode: false,
583
- maxTokens: 32768
584
- },
585
- // GPT-3.5
586
- "gpt-3.5-turbo": {
587
- vision: false,
588
- tools: true,
589
- jsonMode: true,
590
- maxTokens: 16385
591
- },
592
- // O1 series (reasoning)
593
- o1: { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
594
- "o1-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 },
595
- "o1-preview": {
596
- vision: true,
597
- tools: false,
598
- jsonMode: false,
599
- maxTokens: 128e3
600
- },
601
- // O3 series
602
- "o3-mini": { vision: true, tools: false, jsonMode: false, maxTokens: 128e3 }
603
- };
604
- function openai(modelId, options = {}) {
605
- const apiKey = options.apiKey ?? process.env.OPENAI_API_KEY;
606
- const baseURL = options.baseURL ?? "https://api.openai.com/v1";
607
- let client = null;
608
- async function getClient() {
609
- if (!client) {
610
- const { default: OpenAI } = await import('openai');
611
- client = new OpenAI({
612
- apiKey,
613
- baseURL,
614
- organization: options.organization,
615
- defaultHeaders: options.headers
616
- });
617
- }
618
- return client;
619
- }
620
- const modelConfig = OPENAI_MODELS[modelId] ?? OPENAI_MODELS["gpt-4o"];
621
- return {
622
- provider: "openai",
623
- modelId,
624
- capabilities: {
625
- supportsVision: modelConfig.vision,
626
- supportsTools: modelConfig.tools,
627
- supportsStreaming: true,
628
- supportsJsonMode: modelConfig.jsonMode,
629
- supportsThinking: false,
630
- supportsPDF: false,
631
- maxTokens: modelConfig.maxTokens,
632
- supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
633
- },
634
- async doGenerate(params) {
635
- const client2 = await getClient();
636
- const messages = formatMessagesForOpenAI(params.messages);
637
- const response = await client2.chat.completions.create({
638
- model: modelId,
639
- messages,
640
- tools: params.tools,
641
- temperature: params.temperature,
642
- max_tokens: params.maxTokens
643
- });
644
- const choice = response.choices[0];
645
- const message = choice.message;
646
- const toolCalls = (message.tool_calls ?? []).map(
647
- (tc) => ({
648
- id: tc.id,
649
- name: tc.function.name,
650
- args: JSON.parse(tc.function.arguments || "{}")
651
- })
652
- );
653
- return {
654
- text: message.content ?? "",
655
- toolCalls,
656
- finishReason: mapFinishReason(choice.finish_reason),
657
- usage: {
658
- promptTokens: response.usage?.prompt_tokens ?? 0,
659
- completionTokens: response.usage?.completion_tokens ?? 0,
660
- totalTokens: response.usage?.total_tokens ?? 0
661
- },
662
- rawResponse: response
663
- };
664
- },
665
- async *doStream(params) {
666
- const client2 = await getClient();
667
- const messages = formatMessagesForOpenAI(params.messages);
668
- const stream = await client2.chat.completions.create({
669
- model: modelId,
670
- messages,
671
- tools: params.tools,
672
- temperature: params.temperature,
673
- max_tokens: params.maxTokens,
674
- stream: true
675
- });
676
- let currentToolCall = null;
677
- let totalPromptTokens = 0;
678
- let totalCompletionTokens = 0;
679
- for await (const chunk of stream) {
680
- if (params.signal?.aborted) {
681
- yield { type: "error", error: new Error("Aborted") };
682
- return;
683
- }
684
- const choice = chunk.choices[0];
685
- const delta = choice?.delta;
686
- if (delta?.content) {
687
- yield { type: "text-delta", text: delta.content };
688
- }
689
- if (delta?.tool_calls) {
690
- for (const tc of delta.tool_calls) {
691
- if (tc.id) {
692
- if (currentToolCall) {
693
- yield {
694
- type: "tool-call",
695
- toolCall: {
696
- id: currentToolCall.id,
697
- name: currentToolCall.name,
698
- args: JSON.parse(currentToolCall.arguments || "{}")
699
- }
700
- };
701
- }
702
- currentToolCall = {
703
- id: tc.id,
704
- name: tc.function?.name ?? "",
705
- arguments: tc.function?.arguments ?? ""
706
- };
707
- } else if (currentToolCall && tc.function?.arguments) {
708
- currentToolCall.arguments += tc.function.arguments;
709
- }
710
- }
711
- }
712
- if (choice?.finish_reason) {
713
- if (currentToolCall) {
714
- yield {
715
- type: "tool-call",
716
- toolCall: {
717
- id: currentToolCall.id,
718
- name: currentToolCall.name,
719
- args: JSON.parse(currentToolCall.arguments || "{}")
720
- }
721
- };
722
- currentToolCall = null;
723
- }
724
- if (chunk.usage) {
725
- totalPromptTokens = chunk.usage.prompt_tokens;
726
- totalCompletionTokens = chunk.usage.completion_tokens;
727
- }
728
- yield {
729
- type: "finish",
730
- finishReason: mapFinishReason(choice.finish_reason),
731
- usage: {
732
- promptTokens: totalPromptTokens,
733
- completionTokens: totalCompletionTokens,
734
- totalTokens: totalPromptTokens + totalCompletionTokens
735
- }
736
- };
737
- }
738
- }
739
- }
740
- };
741
- }
742
- function mapFinishReason(reason) {
743
- switch (reason) {
744
- case "stop":
745
- return "stop";
746
- case "length":
747
- return "length";
748
- case "tool_calls":
749
- case "function_call":
750
- return "tool-calls";
751
- case "content_filter":
752
- return "content-filter";
753
- default:
754
- return "unknown";
755
- }
756
- }
757
- function formatMessagesForOpenAI(messages) {
758
- return messages.map((msg) => {
759
- switch (msg.role) {
760
- case "system":
761
- return { role: "system", content: msg.content };
762
- case "user":
763
- if (typeof msg.content === "string") {
764
- return { role: "user", content: msg.content };
765
- }
766
- return {
767
- role: "user",
768
- content: msg.content.map((part) => {
769
- if (part.type === "text") {
770
- return { type: "text", text: part.text };
771
- }
772
- if (part.type === "image") {
773
- const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
774
- const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
775
- return { type: "image_url", image_url: { url, detail: "auto" } };
776
- }
777
- return { type: "text", text: "" };
778
- })
779
- };
780
- case "assistant":
781
- const assistantMsg = {
782
- role: "assistant",
783
- content: msg.content
784
- };
785
- if (msg.toolCalls && msg.toolCalls.length > 0) {
786
- assistantMsg.tool_calls = msg.toolCalls.map((tc) => ({
787
- id: tc.id,
788
- type: "function",
789
- function: {
790
- name: tc.name,
791
- arguments: JSON.stringify(tc.args)
792
- }
793
- }));
794
- }
795
- return assistantMsg;
796
- case "tool":
797
- return {
798
- role: "tool",
799
- tool_call_id: msg.toolCallId,
800
- content: msg.content
801
- };
802
- default:
803
- return msg;
804
- }
805
- });
806
- }
807
-
808
- // src/providers/anthropic/provider.ts
809
- var ANTHROPIC_MODELS = {
810
- // Claude 4 series
811
- "claude-sonnet-4-20250514": {
812
- vision: true,
813
- tools: true,
814
- thinking: true,
815
- pdf: true,
816
- maxTokens: 2e5
817
- },
818
- "claude-opus-4-20250514": {
819
- vision: true,
820
- tools: true,
821
- thinking: true,
822
- pdf: true,
823
- maxTokens: 2e5
824
- },
825
- // Claude 3.7 series
826
- "claude-3-7-sonnet-20250219": {
827
- vision: true,
828
- tools: true,
829
- thinking: true,
830
- pdf: true,
831
- maxTokens: 2e5
832
- },
833
- "claude-3-7-sonnet-latest": {
834
- vision: true,
835
- tools: true,
836
- thinking: true,
837
- pdf: true,
838
- maxTokens: 2e5
839
- },
840
- // Claude 3.5 series
841
- "claude-3-5-sonnet-20241022": {
842
- vision: true,
843
- tools: true,
844
- thinking: false,
845
- pdf: true,
846
- maxTokens: 2e5
847
- },
848
- "claude-3-5-sonnet-latest": {
849
- vision: true,
850
- tools: true,
851
- thinking: false,
852
- pdf: true,
853
- maxTokens: 2e5
854
- },
855
- "claude-3-5-haiku-20241022": {
856
- vision: true,
857
- tools: true,
858
- thinking: false,
859
- pdf: false,
860
- maxTokens: 2e5
861
- },
862
- "claude-3-5-haiku-latest": {
863
- vision: true,
864
- tools: true,
865
- thinking: false,
866
- pdf: false,
867
- maxTokens: 2e5
868
- },
869
- // Claude 3 series
870
- "claude-3-opus-20240229": {
871
- vision: true,
872
- tools: true,
873
- thinking: false,
874
- pdf: false,
875
- maxTokens: 2e5
876
- },
877
- "claude-3-sonnet-20240229": {
878
- vision: true,
879
- tools: true,
880
- thinking: false,
881
- pdf: false,
882
- maxTokens: 2e5
883
- },
884
- "claude-3-haiku-20240307": {
885
- vision: true,
886
- tools: true,
887
- thinking: false,
888
- pdf: false,
889
- maxTokens: 2e5
890
- }
891
- };
892
- function anthropic(modelId, options = {}) {
893
- const apiKey = options.apiKey ?? process.env.ANTHROPIC_API_KEY;
894
- let client = null;
895
- async function getClient() {
896
- if (!client) {
897
- const { default: Anthropic } = await import('@anthropic-ai/sdk');
898
- client = new Anthropic({
899
- apiKey,
900
- baseURL: options.baseURL
901
- });
902
- }
903
- return client;
904
- }
905
- const modelConfig = ANTHROPIC_MODELS[modelId] ?? ANTHROPIC_MODELS["claude-3-5-sonnet-latest"];
906
- return {
907
- provider: "anthropic",
908
- modelId,
909
- capabilities: {
910
- supportsVision: modelConfig.vision,
911
- supportsTools: modelConfig.tools,
912
- supportsStreaming: true,
913
- supportsJsonMode: false,
914
- supportsThinking: modelConfig.thinking,
915
- supportsPDF: modelConfig.pdf,
916
- maxTokens: modelConfig.maxTokens,
917
- supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
918
- },
919
- async doGenerate(params) {
920
- const client2 = await getClient();
921
- const { system, messages } = formatMessagesForAnthropic(params.messages);
922
- const requestOptions = {
923
- model: modelId,
924
- max_tokens: params.maxTokens ?? 4096,
925
- system: system || void 0,
926
- messages,
927
- tools: params.tools
928
- };
929
- if (params.temperature !== void 0) {
930
- requestOptions.temperature = params.temperature;
931
- }
932
- if (options.thinking?.enabled && modelConfig.thinking) {
933
- requestOptions.thinking = {
934
- type: "enabled",
935
- budget_tokens: options.thinking.budgetTokens ?? 1e4
936
- };
937
- }
938
- const response = await client2.messages.create(requestOptions);
939
- let text = "";
940
- const toolCalls = [];
941
- for (const block of response.content) {
942
- if (block.type === "text") {
943
- text += block.text;
944
- } else if (block.type === "tool_use") {
945
- toolCalls.push({
946
- id: block.id,
947
- name: block.name,
948
- args: block.input
949
- });
950
- }
951
- }
952
- return {
953
- text,
954
- toolCalls,
955
- finishReason: mapFinishReason2(response.stop_reason),
956
- usage: {
957
- promptTokens: response.usage?.input_tokens ?? 0,
958
- completionTokens: response.usage?.output_tokens ?? 0,
959
- totalTokens: (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0)
960
- },
961
- rawResponse: response
962
- };
963
- },
964
- async *doStream(params) {
965
- const client2 = await getClient();
966
- const { system, messages } = formatMessagesForAnthropic(params.messages);
967
- const requestOptions = {
968
- model: modelId,
969
- max_tokens: params.maxTokens ?? 4096,
970
- system: system || void 0,
971
- messages,
972
- tools: params.tools
973
- };
974
- if (params.temperature !== void 0) {
975
- requestOptions.temperature = params.temperature;
976
- }
977
- if (options.thinking?.enabled && modelConfig.thinking) {
978
- requestOptions.thinking = {
979
- type: "enabled",
980
- budget_tokens: options.thinking.budgetTokens ?? 1e4
981
- };
982
- }
983
- const stream = await client2.messages.stream(requestOptions);
984
- let currentToolUse = null;
985
- let inputTokens = 0;
986
- let outputTokens = 0;
987
- for await (const event of stream) {
988
- if (params.signal?.aborted) {
989
- yield { type: "error", error: new Error("Aborted") };
990
- return;
991
- }
992
- switch (event.type) {
993
- case "message_start":
994
- if (event.message?.usage) {
995
- inputTokens = event.message.usage.input_tokens ?? 0;
996
- }
997
- break;
998
- case "content_block_start":
999
- if (event.content_block?.type === "tool_use") {
1000
- currentToolUse = {
1001
- id: event.content_block.id,
1002
- name: event.content_block.name,
1003
- input: ""
1004
- };
1005
- }
1006
- break;
1007
- case "content_block_delta":
1008
- if (event.delta?.type === "text_delta") {
1009
- yield { type: "text-delta", text: event.delta.text };
1010
- } else if (event.delta?.type === "input_json_delta" && currentToolUse) {
1011
- currentToolUse.input += event.delta.partial_json;
1012
- }
1013
- break;
1014
- case "content_block_stop":
1015
- if (currentToolUse) {
1016
- yield {
1017
- type: "tool-call",
1018
- toolCall: {
1019
- id: currentToolUse.id,
1020
- name: currentToolUse.name,
1021
- args: JSON.parse(currentToolUse.input || "{}")
1022
- }
1023
- };
1024
- currentToolUse = null;
1025
- }
1026
- break;
1027
- case "message_delta":
1028
- if (event.usage) {
1029
- outputTokens = event.usage.output_tokens ?? 0;
1030
- }
1031
- if (event.delta?.stop_reason) {
1032
- yield {
1033
- type: "finish",
1034
- finishReason: mapFinishReason2(event.delta.stop_reason),
1035
- usage: {
1036
- promptTokens: inputTokens,
1037
- completionTokens: outputTokens,
1038
- totalTokens: inputTokens + outputTokens
1039
- }
1040
- };
1041
- }
1042
- break;
1043
- }
1044
- }
1045
- }
1046
- };
1047
- }
1048
- function mapFinishReason2(reason) {
1049
- switch (reason) {
1050
- case "end_turn":
1051
- case "stop_sequence":
1052
- return "stop";
1053
- case "max_tokens":
1054
- return "length";
1055
- case "tool_use":
1056
- return "tool-calls";
1057
- default:
1058
- return "unknown";
1059
- }
1060
- }
1061
- function formatMessagesForAnthropic(messages) {
1062
- let system = "";
1063
- const formatted = [];
1064
- const pendingToolResults = [];
1065
- for (const msg of messages) {
1066
- if (msg.role === "system") {
1067
- system += (system ? "\n" : "") + msg.content;
1068
- continue;
1069
- }
1070
- if (msg.role === "assistant" && pendingToolResults.length > 0) {
1071
- formatted.push({
1072
- role: "user",
1073
- content: pendingToolResults.map((tr) => ({
1074
- type: "tool_result",
1075
- tool_use_id: tr.toolCallId,
1076
- content: tr.content
1077
- }))
1078
- });
1079
- pendingToolResults.length = 0;
1080
- }
1081
- if (msg.role === "user") {
1082
- if (pendingToolResults.length > 0) {
1083
- formatted.push({
1084
- role: "user",
1085
- content: pendingToolResults.map((tr) => ({
1086
- type: "tool_result",
1087
- tool_use_id: tr.toolCallId,
1088
- content: tr.content
1089
- }))
1090
- });
1091
- pendingToolResults.length = 0;
1092
- }
1093
- if (typeof msg.content === "string") {
1094
- formatted.push({ role: "user", content: msg.content });
1095
- } else {
1096
- const content = [];
1097
- for (const part of msg.content) {
1098
- if (part.type === "text") {
1099
- content.push({ type: "text", text: part.text });
1100
- } else if (part.type === "image") {
1101
- const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
1102
- if (imageData.startsWith("http")) {
1103
- content.push({
1104
- type: "image",
1105
- source: { type: "url", url: imageData }
1106
- });
1107
- } else {
1108
- const base64 = imageData.startsWith("data:") ? imageData.split(",")[1] : imageData;
1109
- content.push({
1110
- type: "image",
1111
- source: {
1112
- type: "base64",
1113
- media_type: part.mimeType ?? "image/png",
1114
- data: base64
1115
- }
1116
- });
1117
- }
1118
- }
1119
- }
1120
- formatted.push({ role: "user", content });
1121
- }
1122
- } else if (msg.role === "assistant") {
1123
- const content = [];
1124
- if (msg.content) {
1125
- content.push({ type: "text", text: msg.content });
1126
- }
1127
- if (msg.toolCalls && msg.toolCalls.length > 0) {
1128
- for (const tc of msg.toolCalls) {
1129
- content.push({
1130
- type: "tool_use",
1131
- id: tc.id,
1132
- name: tc.name,
1133
- input: tc.args
1134
- });
1135
- }
1136
- }
1137
- if (content.length > 0) {
1138
- formatted.push({ role: "assistant", content });
1139
- }
1140
- } else if (msg.role === "tool") {
1141
- pendingToolResults.push({
1142
- toolCallId: msg.toolCallId,
1143
- content: msg.content
1144
- });
1145
- }
1146
- }
1147
- if (pendingToolResults.length > 0) {
1148
- formatted.push({
1149
- role: "user",
1150
- content: pendingToolResults.map((tr) => ({
1151
- type: "tool_result",
1152
- tool_use_id: tr.toolCallId,
1153
- content: tr.content
1154
- }))
1155
- });
1156
- }
1157
- return { system, messages: formatted };
1158
- }
1159
-
1160
- // src/providers/xai/provider.ts
1161
- var XAI_MODELS = {
1162
- // Grok 4.1 Fast (Latest - December 2025)
1163
- "grok-4-1-fast-reasoning": { vision: false, tools: true, maxTokens: 2e6 },
1164
- "grok-4-1-fast-non-reasoning": {
1165
- vision: false,
1166
- tools: true,
1167
- maxTokens: 2e6
1168
- },
1169
- // Grok 4 Fast (September 2025)
1170
- "grok-4-fast-reasoning": { vision: false, tools: true, maxTokens: 2e6 },
1171
- "grok-4-fast-non-reasoning": {
1172
- vision: false,
1173
- tools: true,
1174
- maxTokens: 2e6
1175
- },
1176
- // Grok 4 (July 2025)
1177
- "grok-4": { vision: true, tools: true, maxTokens: 256e3 },
1178
- "grok-4-0709": { vision: true, tools: true, maxTokens: 256e3 },
1179
- // Grok 3 (February 2025) - Stable
1180
- "grok-3-beta": { vision: true, tools: true, maxTokens: 131072 },
1181
- "grok-3-fast-beta": { vision: false, tools: true, maxTokens: 131072 },
1182
- "grok-3-mini-beta": { vision: false, tools: true, maxTokens: 32768 },
1183
- "grok-3-mini-fast-beta": { vision: false, tools: true, maxTokens: 32768 },
1184
- // Grok Code Fast (August 2025)
1185
- "grok-code-fast-1": { vision: false, tools: true, maxTokens: 256e3 },
1186
- // Grok 2 (Legacy)
1187
- "grok-2": { vision: true, tools: true, maxTokens: 131072 },
1188
- "grok-2-latest": { vision: true, tools: true, maxTokens: 131072 },
1189
- "grok-2-mini": { vision: false, tools: true, maxTokens: 131072 }
1190
- };
1191
- function xai(modelId, options = {}) {
1192
- const apiKey = options.apiKey ?? process.env.XAI_API_KEY;
1193
- const baseURL = options.baseURL ?? "https://api.x.ai/v1";
1194
- let client = null;
1195
- async function getClient() {
1196
- if (!client) {
1197
- const { default: OpenAI } = await import('openai');
1198
- client = new OpenAI({
1199
- apiKey,
1200
- baseURL
1201
- });
1202
- }
1203
- return client;
1204
- }
1205
- const modelConfig = XAI_MODELS[modelId] ?? XAI_MODELS["grok-3-fast-beta"];
1206
- return {
1207
- provider: "xai",
1208
- modelId,
1209
- capabilities: {
1210
- supportsVision: modelConfig.vision,
1211
- supportsTools: modelConfig.tools,
1212
- supportsStreaming: true,
1213
- supportsJsonMode: false,
1214
- // xAI doesn't support JSON mode yet
1215
- supportsThinking: false,
1216
- supportsPDF: false,
1217
- maxTokens: modelConfig.maxTokens,
1218
- supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
1219
- },
1220
- async doGenerate(params) {
1221
- const client2 = await getClient();
1222
- const messages = formatMessagesForXAI(params.messages);
1223
- const response = await client2.chat.completions.create({
1224
- model: modelId,
1225
- messages,
1226
- tools: params.tools,
1227
- temperature: params.temperature,
1228
- max_tokens: params.maxTokens
1229
- });
1230
- const choice = response.choices[0];
1231
- const message = choice.message;
1232
- const toolCalls = (message.tool_calls ?? []).map(
1233
- (tc) => ({
1234
- id: tc.id,
1235
- name: tc.function.name,
1236
- args: JSON.parse(tc.function.arguments || "{}")
1237
- })
1238
- );
1239
- return {
1240
- text: message.content ?? "",
1241
- toolCalls,
1242
- finishReason: mapFinishReason3(choice.finish_reason),
1243
- usage: {
1244
- promptTokens: response.usage?.prompt_tokens ?? 0,
1245
- completionTokens: response.usage?.completion_tokens ?? 0,
1246
- totalTokens: response.usage?.total_tokens ?? 0
1247
- },
1248
- rawResponse: response
1249
- };
1250
- },
1251
- async *doStream(params) {
1252
- const client2 = await getClient();
1253
- const messages = formatMessagesForXAI(params.messages);
1254
- const stream = await client2.chat.completions.create({
1255
- model: modelId,
1256
- messages,
1257
- tools: params.tools,
1258
- temperature: params.temperature,
1259
- max_tokens: params.maxTokens,
1260
- stream: true
1261
- });
1262
- let currentToolCall = null;
1263
- let totalPromptTokens = 0;
1264
- let totalCompletionTokens = 0;
1265
- for await (const chunk of stream) {
1266
- if (params.signal?.aborted) {
1267
- yield { type: "error", error: new Error("Aborted") };
1268
- return;
1269
- }
1270
- const choice = chunk.choices[0];
1271
- const delta = choice?.delta;
1272
- if (delta?.content) {
1273
- yield { type: "text-delta", text: delta.content };
1274
- }
1275
- if (delta?.tool_calls) {
1276
- for (const tc of delta.tool_calls) {
1277
- if (tc.id) {
1278
- if (currentToolCall) {
1279
- yield {
1280
- type: "tool-call",
1281
- toolCall: {
1282
- id: currentToolCall.id,
1283
- name: currentToolCall.name,
1284
- args: JSON.parse(currentToolCall.arguments || "{}")
1285
- }
1286
- };
1287
- }
1288
- currentToolCall = {
1289
- id: tc.id,
1290
- name: tc.function?.name ?? "",
1291
- arguments: tc.function?.arguments ?? ""
1292
- };
1293
- } else if (currentToolCall && tc.function?.arguments) {
1294
- currentToolCall.arguments += tc.function.arguments;
1295
- }
1296
- }
1297
- }
1298
- if (choice?.finish_reason) {
1299
- if (currentToolCall) {
1300
- yield {
1301
- type: "tool-call",
1302
- toolCall: {
1303
- id: currentToolCall.id,
1304
- name: currentToolCall.name,
1305
- args: JSON.parse(currentToolCall.arguments || "{}")
1306
- }
1307
- };
1308
- currentToolCall = null;
1309
- }
1310
- if (chunk.usage) {
1311
- totalPromptTokens = chunk.usage.prompt_tokens;
1312
- totalCompletionTokens = chunk.usage.completion_tokens;
1313
- }
1314
- yield {
1315
- type: "finish",
1316
- finishReason: mapFinishReason3(choice.finish_reason),
1317
- usage: {
1318
- promptTokens: totalPromptTokens,
1319
- completionTokens: totalCompletionTokens,
1320
- totalTokens: totalPromptTokens + totalCompletionTokens
1321
- }
1322
- };
1323
- }
1324
- }
1325
- }
1326
- };
1327
- }
1328
- function mapFinishReason3(reason) {
1329
- switch (reason) {
1330
- case "stop":
1331
- return "stop";
1332
- case "length":
1333
- return "length";
1334
- case "tool_calls":
1335
- case "function_call":
1336
- return "tool-calls";
1337
- case "content_filter":
1338
- return "content-filter";
1339
- default:
1340
- return "unknown";
1341
- }
1342
- }
1343
- function formatMessagesForXAI(messages) {
1344
- return messages.map((msg) => {
1345
- switch (msg.role) {
1346
- case "system":
1347
- return { role: "system", content: msg.content };
1348
- case "user":
1349
- if (typeof msg.content === "string") {
1350
- return { role: "user", content: msg.content };
1351
- }
1352
- return {
1353
- role: "user",
1354
- content: msg.content.map((part) => {
1355
- if (part.type === "text") {
1356
- return { type: "text", text: part.text };
1357
- }
1358
- if (part.type === "image") {
1359
- const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
1360
- const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
1361
- return { type: "image_url", image_url: { url, detail: "auto" } };
1362
- }
1363
- return { type: "text", text: "" };
1364
- })
1365
- };
1366
- case "assistant":
1367
- const assistantMsg = {
1368
- role: "assistant",
1369
- content: msg.content
1370
- };
1371
- if (msg.toolCalls && msg.toolCalls.length > 0) {
1372
- assistantMsg.tool_calls = msg.toolCalls.map((tc) => ({
1373
- id: tc.id,
1374
- type: "function",
1375
- function: {
1376
- name: tc.name,
1377
- arguments: JSON.stringify(tc.args)
1378
- }
1379
- }));
1380
- }
1381
- return assistantMsg;
1382
- case "tool":
1383
- return {
1384
- role: "tool",
1385
- tool_call_id: msg.toolCallId,
1386
- content: msg.content
1387
- };
1388
- default:
1389
- return msg;
1390
- }
1391
- });
1392
- }
1393
-
1394
- // src/providers/google/provider.ts
1395
- var GOOGLE_MODELS = {
1396
- // Gemini 2.5 (Experimental)
1397
- "gemini-2.5-pro-preview-05-06": {
1398
- vision: true,
1399
- tools: true,
1400
- audio: true,
1401
- video: true,
1402
- maxTokens: 1048576
1403
- },
1404
- "gemini-2.5-flash-preview-05-20": {
1405
- vision: true,
1406
- tools: true,
1407
- audio: true,
1408
- video: true,
1409
- maxTokens: 1048576
1410
- },
1411
- // Gemini 2.0
1412
- "gemini-2.0-flash": {
1413
- vision: true,
1414
- tools: true,
1415
- audio: true,
1416
- video: true,
1417
- maxTokens: 1048576
1418
- },
1419
- "gemini-2.0-flash-exp": {
1420
- vision: true,
1421
- tools: true,
1422
- audio: true,
1423
- video: true,
1424
- maxTokens: 1048576
1425
- },
1426
- "gemini-2.0-flash-lite": {
1427
- vision: true,
1428
- tools: true,
1429
- audio: false,
1430
- video: false,
1431
- maxTokens: 1048576
1432
- },
1433
- "gemini-2.0-flash-thinking-exp": {
1434
- vision: true,
1435
- tools: false,
1436
- audio: false,
1437
- video: false,
1438
- maxTokens: 32767
1439
- },
1440
- // Gemini 1.5
1441
- "gemini-1.5-pro": {
1442
- vision: true,
1443
- tools: true,
1444
- audio: true,
1445
- video: true,
1446
- maxTokens: 2097152
1447
- },
1448
- "gemini-1.5-pro-latest": {
1449
- vision: true,
1450
- tools: true,
1451
- audio: true,
1452
- video: true,
1453
- maxTokens: 2097152
1454
- },
1455
- "gemini-1.5-flash": {
1456
- vision: true,
1457
- tools: true,
1458
- audio: true,
1459
- video: true,
1460
- maxTokens: 1048576
1461
- },
1462
- "gemini-1.5-flash-latest": {
1463
- vision: true,
1464
- tools: true,
1465
- audio: true,
1466
- video: true,
1467
- maxTokens: 1048576
1468
- },
1469
- "gemini-1.5-flash-8b": {
1470
- vision: true,
1471
- tools: true,
1472
- audio: false,
1473
- video: false,
1474
- maxTokens: 1048576
1475
- }
1476
- };
1477
- function google(modelId, options = {}) {
1478
- const apiKey = options.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
1479
- const baseURL = options.baseURL ?? "https://generativelanguage.googleapis.com/v1beta/openai/";
1480
- let client = null;
1481
- async function getClient() {
1482
- if (!client) {
1483
- const { default: OpenAI } = await import('openai');
1484
- client = new OpenAI({
1485
- apiKey,
1486
- baseURL
1487
- });
1488
- }
1489
- return client;
1490
- }
1491
- const modelConfig = GOOGLE_MODELS[modelId] ?? GOOGLE_MODELS["gemini-2.0-flash"];
1492
- return {
1493
- provider: "google",
1494
- modelId,
1495
- capabilities: {
1496
- supportsVision: modelConfig.vision,
1497
- supportsTools: modelConfig.tools,
1498
- supportsStreaming: true,
1499
- supportsJsonMode: true,
1500
- supportsThinking: modelId.includes("thinking"),
1501
- supportsPDF: true,
1502
- maxTokens: modelConfig.maxTokens,
1503
- supportedImageTypes: modelConfig.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : []
1504
- },
1505
- async doGenerate(params) {
1506
- const client2 = await getClient();
1507
- const messages = formatMessagesForGoogle(params.messages);
1508
- const response = await client2.chat.completions.create({
1509
- model: modelId,
1510
- messages,
1511
- tools: params.tools,
1512
- temperature: params.temperature,
1513
- max_tokens: params.maxTokens
1514
- });
1515
- const choice = response.choices[0];
1516
- const message = choice.message;
1517
- const toolCalls = (message.tool_calls ?? []).map(
1518
- (tc) => ({
1519
- id: tc.id,
1520
- name: tc.function.name,
1521
- args: JSON.parse(tc.function.arguments || "{}")
1522
- })
1523
- );
1524
- return {
1525
- text: message.content ?? "",
1526
- toolCalls,
1527
- finishReason: mapFinishReason4(choice.finish_reason),
1528
- usage: {
1529
- promptTokens: response.usage?.prompt_tokens ?? 0,
1530
- completionTokens: response.usage?.completion_tokens ?? 0,
1531
- totalTokens: response.usage?.total_tokens ?? 0
1532
- },
1533
- rawResponse: response
1534
- };
1535
- },
1536
- async *doStream(params) {
1537
- const client2 = await getClient();
1538
- const messages = formatMessagesForGoogle(params.messages);
1539
- const stream = await client2.chat.completions.create({
1540
- model: modelId,
1541
- messages,
1542
- tools: params.tools,
1543
- temperature: params.temperature,
1544
- max_tokens: params.maxTokens,
1545
- stream: true
1546
- });
1547
- let currentToolCall = null;
1548
- let totalPromptTokens = 0;
1549
- let totalCompletionTokens = 0;
1550
- for await (const chunk of stream) {
1551
- if (params.signal?.aborted) {
1552
- yield { type: "error", error: new Error("Aborted") };
1553
- return;
1554
- }
1555
- const choice = chunk.choices[0];
1556
- const delta = choice?.delta;
1557
- if (delta?.content) {
1558
- yield { type: "text-delta", text: delta.content };
1559
- }
1560
- if (delta?.tool_calls) {
1561
- for (const tc of delta.tool_calls) {
1562
- if (tc.id) {
1563
- if (currentToolCall) {
1564
- yield {
1565
- type: "tool-call",
1566
- toolCall: {
1567
- id: currentToolCall.id,
1568
- name: currentToolCall.name,
1569
- args: JSON.parse(currentToolCall.arguments || "{}")
1570
- }
1571
- };
1572
- }
1573
- currentToolCall = {
1574
- id: tc.id,
1575
- name: tc.function?.name ?? "",
1576
- arguments: tc.function?.arguments ?? ""
1577
- };
1578
- } else if (currentToolCall && tc.function?.arguments) {
1579
- currentToolCall.arguments += tc.function.arguments;
1580
- }
1581
- }
1582
- }
1583
- if (choice?.finish_reason) {
1584
- if (currentToolCall) {
1585
- yield {
1586
- type: "tool-call",
1587
- toolCall: {
1588
- id: currentToolCall.id,
1589
- name: currentToolCall.name,
1590
- args: JSON.parse(currentToolCall.arguments || "{}")
1591
- }
1592
- };
1593
- currentToolCall = null;
1594
- }
1595
- if (chunk.usage) {
1596
- totalPromptTokens = chunk.usage.prompt_tokens;
1597
- totalCompletionTokens = chunk.usage.completion_tokens;
1598
- }
1599
- yield {
1600
- type: "finish",
1601
- finishReason: mapFinishReason4(choice.finish_reason),
1602
- usage: {
1603
- promptTokens: totalPromptTokens,
1604
- completionTokens: totalCompletionTokens,
1605
- totalTokens: totalPromptTokens + totalCompletionTokens
1606
- }
1607
- };
1608
- }
1609
- }
1610
- }
1611
- };
1612
- }
1613
- function mapFinishReason4(reason) {
1614
- switch (reason) {
1615
- case "stop":
1616
- return "stop";
1617
- case "length":
1618
- return "length";
1619
- case "tool_calls":
1620
- case "function_call":
1621
- return "tool-calls";
1622
- case "content_filter":
1623
- return "content-filter";
1624
- default:
1625
- return "unknown";
1626
- }
1627
- }
1628
- function formatMessagesForGoogle(messages) {
1629
- return messages.map((msg) => {
1630
- switch (msg.role) {
1631
- case "system":
1632
- return { role: "system", content: msg.content };
1633
- case "user":
1634
- if (typeof msg.content === "string") {
1635
- return { role: "user", content: msg.content };
1636
- }
1637
- return {
1638
- role: "user",
1639
- content: msg.content.map((part) => {
1640
- if (part.type === "text") {
1641
- return { type: "text", text: part.text };
1642
- }
1643
- if (part.type === "image") {
1644
- const imageData = typeof part.image === "string" ? part.image : Buffer.from(part.image).toString("base64");
1645
- const url = imageData.startsWith("data:") ? imageData : `data:${part.mimeType ?? "image/png"};base64,${imageData}`;
1646
- return { type: "image_url", image_url: { url, detail: "auto" } };
1647
- }
1648
- return { type: "text", text: "" };
1649
- })
1650
- };
1651
- case "assistant":
1652
- const assistantMsg = {
1653
- role: "assistant",
1654
- content: msg.content
1655
- };
1656
- if (msg.toolCalls && msg.toolCalls.length > 0) {
1657
- assistantMsg.tool_calls = msg.toolCalls.map((tc) => ({
1658
- id: tc.id,
1659
- type: "function",
1660
- function: {
1661
- name: tc.name,
1662
- arguments: JSON.stringify(tc.args)
1663
- }
1664
- }));
1665
- }
1666
- return assistantMsg;
1667
- case "tool":
1668
- return {
1669
- role: "tool",
1670
- tool_call_id: msg.toolCallId,
1671
- content: msg.content
1672
- };
1673
- default:
1674
- return msg;
1675
- }
1676
- });
1677
- }
1678
-
1679
- // src/adapters/base.ts
1680
- function formatMessages(messages, systemPrompt) {
1681
- const formatted = [];
1682
- if (systemPrompt) {
1683
- formatted.push({ role: "system", content: systemPrompt });
1684
- }
1685
- for (const msg of messages) {
1686
- formatted.push({
1687
- role: msg.role,
1688
- content: msg.content ?? ""
1689
- });
1690
- }
1691
- return formatted;
1692
- }
1693
- function parameterToJsonSchema(param) {
1694
- const schema = {
1695
- type: param.type
1696
- };
1697
- if (param.description) {
1698
- schema.description = param.description;
1699
- }
1700
- if (param.enum) {
1701
- schema.enum = param.enum;
1702
- }
1703
- if (param.type === "array" && param.items) {
1704
- schema.items = parameterToJsonSchema(
1705
- param.items
1706
- );
1707
- }
1708
- if (param.type === "object" && param.properties) {
1709
- schema.properties = Object.fromEntries(
1710
- Object.entries(param.properties).map(([key, prop]) => [
1711
- key,
1712
- parameterToJsonSchema(
1713
- prop
1714
- )
1715
- ])
1716
- );
1717
- }
1718
- return schema;
1719
- }
1720
- function formatTools(actions) {
1721
- return actions.map((action) => ({
1722
- type: "function",
1723
- function: {
1724
- name: action.name,
1725
- description: action.description,
1726
- parameters: {
1727
- type: "object",
1728
- properties: action.parameters ? Object.fromEntries(
1729
- Object.entries(action.parameters).map(([key, param]) => [
1730
- key,
1731
- parameterToJsonSchema(param)
1732
- ])
1733
- ) : {},
1734
- required: action.parameters ? Object.entries(action.parameters).filter(([, param]) => param.required).map(([key]) => key) : []
1735
- }
1736
- }
1737
- }));
1738
- }
1739
- function hasImageAttachments(message) {
1740
- const attachments = message.metadata?.attachments;
1741
- return attachments?.some((a) => a.type === "image") ?? false;
1742
- }
1743
- function hasMediaAttachments(message) {
1744
- const attachments = message.metadata?.attachments;
1745
- return attachments?.some(
1746
- (a) => a.type === "image" || a.type === "file" && a.mimeType === "application/pdf"
1747
- ) ?? false;
1748
- }
1749
- function attachmentToAnthropicImage(attachment) {
1750
- if (attachment.type !== "image") return null;
1751
- if (attachment.url) {
1752
- return {
1753
- type: "image",
1754
- source: {
1755
- type: "url",
1756
- url: attachment.url
1757
- }
1758
- };
1759
- }
1760
- if (!attachment.data) return null;
1761
- let base64Data = attachment.data;
1762
- if (base64Data.startsWith("data:")) {
1763
- const commaIndex = base64Data.indexOf(",");
1764
- if (commaIndex !== -1) {
1765
- base64Data = base64Data.slice(commaIndex + 1);
1766
- }
1767
- }
1768
- return {
1769
- type: "image",
1770
- source: {
1771
- type: "base64",
1772
- media_type: attachment.mimeType || "image/png",
1773
- data: base64Data
1774
- }
1775
- };
1776
- }
1777
- function attachmentToOpenAIImage(attachment) {
1778
- if (attachment.type !== "image") return null;
1779
- let imageUrl;
1780
- if (attachment.url) {
1781
- imageUrl = attachment.url;
1782
- } else if (attachment.data) {
1783
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
1784
- } else {
1785
- return null;
1786
- }
1787
- return {
1788
- type: "image_url",
1789
- image_url: {
1790
- url: imageUrl,
1791
- detail: "auto"
1792
- }
1793
- };
1794
- }
1795
- function attachmentToAnthropicDocument(attachment) {
1796
- if (attachment.type !== "file" || attachment.mimeType !== "application/pdf") {
1797
- return null;
1798
- }
1799
- if (attachment.url) {
1800
- return {
1801
- type: "document",
1802
- source: {
1803
- type: "url",
1804
- url: attachment.url
1805
- }
1806
- };
1807
- }
1808
- if (!attachment.data) return null;
1809
- let base64Data = attachment.data;
1810
- if (base64Data.startsWith("data:")) {
1811
- const commaIndex = base64Data.indexOf(",");
1812
- if (commaIndex !== -1) {
1813
- base64Data = base64Data.slice(commaIndex + 1);
1814
- }
1815
- }
1816
- return {
1817
- type: "document",
1818
- source: {
1819
- type: "base64",
1820
- media_type: "application/pdf",
1821
- data: base64Data
1822
- }
1823
- };
1824
- }
1825
- function messageToAnthropicContent(message) {
1826
- const attachments = message.metadata?.attachments;
1827
- const content = message.content ?? "";
1828
- if (!hasMediaAttachments(message)) {
1829
- return content;
1830
- }
1831
- const blocks = [];
1832
- if (attachments) {
1833
- for (const attachment of attachments) {
1834
- const imageBlock = attachmentToAnthropicImage(attachment);
1835
- if (imageBlock) {
1836
- blocks.push(imageBlock);
1837
- continue;
1838
- }
1839
- const docBlock = attachmentToAnthropicDocument(attachment);
1840
- if (docBlock) {
1841
- blocks.push(docBlock);
1842
- }
1843
- }
1844
- }
1845
- if (content) {
1846
- blocks.push({ type: "text", text: content });
1847
- }
1848
- return blocks;
1849
- }
1850
- function messageToOpenAIContent(message) {
1851
- const attachments = message.metadata?.attachments;
1852
- const content = message.content ?? "";
1853
- if (!hasImageAttachments(message)) {
1854
- return content;
1855
- }
1856
- const blocks = [];
1857
- if (content) {
1858
- blocks.push({ type: "text", text: content });
1859
- }
1860
- if (attachments) {
1861
- for (const attachment of attachments) {
1862
- const imageBlock = attachmentToOpenAIImage(attachment);
1863
- if (imageBlock) {
1864
- blocks.push(imageBlock);
1865
- }
1866
- }
1867
- }
1868
- return blocks;
1869
- }
1870
- function formatMessagesForAnthropic2(messages, systemPrompt) {
1871
- const formatted = [];
1872
- for (let i = 0; i < messages.length; i++) {
1873
- const msg = messages[i];
1874
- if (msg.role === "system") continue;
1875
- if (msg.role === "assistant") {
1876
- const content = [];
1877
- if (msg.content) {
1878
- content.push({ type: "text", text: msg.content });
1879
- }
1880
- if (msg.tool_calls && msg.tool_calls.length > 0) {
1881
- for (const tc of msg.tool_calls) {
1882
- content.push({
1883
- type: "tool_use",
1884
- id: tc.id,
1885
- name: tc.function.name,
1886
- input: JSON.parse(tc.function.arguments)
1887
- });
1888
- }
1889
- }
1890
- formatted.push({
1891
- role: "assistant",
1892
- content: content.length === 1 && content[0].type === "text" ? content[0].text : content
1893
- });
1894
- } else if (msg.role === "tool" && msg.tool_call_id) {
1895
- const toolResults = [
1896
- {
1897
- type: "tool_result",
1898
- tool_use_id: msg.tool_call_id,
1899
- content: msg.content ?? ""
1900
- }
1901
- ];
1902
- while (i + 1 < messages.length && messages[i + 1].role === "tool") {
1903
- i++;
1904
- const nextTool = messages[i];
1905
- if (nextTool.tool_call_id) {
1906
- toolResults.push({
1907
- type: "tool_result",
1908
- tool_use_id: nextTool.tool_call_id,
1909
- content: nextTool.content ?? ""
1910
- });
1911
- }
1912
- }
1913
- formatted.push({
1914
- role: "user",
1915
- content: toolResults
1916
- });
1917
- } else if (msg.role === "user") {
1918
- formatted.push({
1919
- role: "user",
1920
- content: messageToAnthropicContent(msg)
1921
- });
1922
- }
1923
- }
1924
- return {
1925
- system: "",
1926
- messages: formatted
1927
- };
1928
- }
1929
- function formatMessagesForOpenAI2(messages, systemPrompt) {
1930
- const formatted = [];
1931
- if (systemPrompt) {
1932
- formatted.push({ role: "system", content: systemPrompt });
1933
- }
1934
- for (const msg of messages) {
1935
- if (msg.role === "system") {
1936
- formatted.push({ role: "system", content: msg.content ?? "" });
1937
- } else if (msg.role === "user") {
1938
- formatted.push({
1939
- role: "user",
1940
- content: messageToOpenAIContent(msg)
1941
- });
1942
- } else if (msg.role === "assistant") {
1943
- const assistantMsg = {
1944
- role: "assistant",
1945
- content: msg.content
1946
- };
1947
- if (msg.tool_calls && msg.tool_calls.length > 0) {
1948
- assistantMsg.tool_calls = msg.tool_calls;
1949
- }
1950
- formatted.push(assistantMsg);
1951
- } else if (msg.role === "tool" && msg.tool_call_id) {
1952
- formatted.push({
1953
- role: "tool",
1954
- content: msg.content ?? "",
1955
- tool_call_id: msg.tool_call_id
1956
- });
1957
- }
1958
- }
1959
- return formatted;
1960
- }
1961
- var OpenAIAdapter = class {
1962
- constructor(config) {
1963
- this.provider = "openai";
1964
- this.config = config;
1965
- this.model = config.model || "gpt-4o";
1966
- }
1967
- async getClient() {
1968
- if (!this.client) {
1969
- const { default: OpenAI } = await import('openai');
1970
- this.client = new OpenAI({
1971
- apiKey: this.config.apiKey,
1972
- baseURL: this.config.baseUrl
1973
- });
1974
- }
1975
- return this.client;
1976
- }
1977
- async *stream(request) {
1978
- const client = await this.getClient();
1979
- let messages;
1980
- if (request.rawMessages && request.rawMessages.length > 0) {
1981
- const processedMessages = request.rawMessages.map((msg) => {
1982
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
1983
- if (hasAttachments) {
1984
- const content = [];
1985
- if (msg.content) {
1986
- content.push({ type: "text", text: msg.content });
1987
- }
1988
- for (const attachment of msg.attachments) {
1989
- if (attachment.type === "image") {
1990
- let imageUrl;
1991
- if (attachment.url) {
1992
- imageUrl = attachment.url;
1993
- } else if (attachment.data) {
1994
- imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
1995
- } else {
1996
- continue;
1997
- }
1998
- content.push({
1999
- type: "image_url",
2000
- image_url: { url: imageUrl, detail: "auto" }
2001
- });
2002
- }
2003
- }
2004
- return { ...msg, content, attachments: void 0 };
2005
- }
2006
- return msg;
2007
- });
2008
- if (request.systemPrompt) {
2009
- const hasSystem = processedMessages.some((m) => m.role === "system");
2010
- if (!hasSystem) {
2011
- messages = [
2012
- { role: "system", content: request.systemPrompt },
2013
- ...processedMessages
2014
- ];
2015
- } else {
2016
- messages = processedMessages;
2017
- }
2018
- } else {
2019
- messages = processedMessages;
2020
- }
2021
- } else {
2022
- messages = formatMessagesForOpenAI2(
2023
- request.messages,
2024
- request.systemPrompt
2025
- );
2026
- }
2027
- const tools = request.actions?.length ? formatTools(request.actions) : void 0;
2028
- const messageId = core.generateMessageId();
2029
- yield { type: "message:start", id: messageId };
2030
- try {
2031
- const stream = await client.chat.completions.create({
2032
- model: request.config?.model || this.model,
2033
- messages,
2034
- tools,
2035
- temperature: request.config?.temperature ?? this.config.temperature,
2036
- max_tokens: request.config?.maxTokens ?? this.config.maxTokens,
2037
- stream: true
2038
- });
2039
- let currentToolCall = null;
2040
- for await (const chunk of stream) {
2041
- if (request.signal?.aborted) {
2042
- break;
2043
- }
2044
- const delta = chunk.choices[0]?.delta;
2045
- if (delta?.content) {
2046
- yield { type: "message:delta", content: delta.content };
2047
- }
2048
- if (delta?.tool_calls) {
2049
- for (const toolCall of delta.tool_calls) {
2050
- if (toolCall.id) {
2051
- if (currentToolCall) {
2052
- yield {
2053
- type: "action:args",
2054
- id: currentToolCall.id,
2055
- args: currentToolCall.arguments
2056
- };
2057
- }
2058
- currentToolCall = {
2059
- id: toolCall.id,
2060
- name: toolCall.function?.name || "",
2061
- arguments: toolCall.function?.arguments || ""
2062
- };
2063
- yield {
2064
- type: "action:start",
2065
- id: currentToolCall.id,
2066
- name: currentToolCall.name
2067
- };
2068
- } else if (currentToolCall && toolCall.function?.arguments) {
2069
- currentToolCall.arguments += toolCall.function.arguments;
2070
- }
2071
- }
2072
- }
2073
- if (chunk.choices[0]?.finish_reason) {
2074
- if (currentToolCall) {
2075
- yield {
2076
- type: "action:args",
2077
- id: currentToolCall.id,
2078
- args: currentToolCall.arguments
2079
- };
2080
- }
2081
- }
2082
- }
2083
- yield { type: "message:end" };
2084
- yield { type: "done" };
2085
- } catch (error) {
2086
- yield {
2087
- type: "error",
2088
- message: error instanceof Error ? error.message : "Unknown error",
2089
- code: "OPENAI_ERROR"
2090
- };
2091
- }
2092
- }
2093
- };
2094
- function createOpenAIAdapter(config) {
2095
- return new OpenAIAdapter(config);
2096
- }
2097
- var AnthropicAdapter = class {
2098
- constructor(config) {
2099
- this.provider = "anthropic";
2100
- this.config = config;
2101
- this.model = config.model || "claude-3-5-sonnet-latest";
2102
- }
2103
- async getClient() {
2104
- if (!this.client) {
2105
- const { default: Anthropic } = await import('@anthropic-ai/sdk');
2106
- this.client = new Anthropic({
2107
- apiKey: this.config.apiKey
2108
- });
2109
- }
2110
- return this.client;
2111
- }
2112
- /**
2113
- * Convert OpenAI-style messages to Anthropic format
2114
- *
2115
- * OpenAI format:
2116
- * - { role: "assistant", content: "...", tool_calls: [...] }
2117
- * - { role: "tool", tool_call_id: "...", content: "..." }
2118
- *
2119
- * Anthropic format:
2120
- * - { role: "assistant", content: [{ type: "text", text: "..." }, { type: "tool_use", id: "...", name: "...", input: {...} }] }
2121
- * - { role: "user", content: [{ type: "tool_result", tool_use_id: "...", content: "..." }] }
2122
- */
2123
- convertToAnthropicMessages(rawMessages) {
2124
- const messages = [];
2125
- const pendingToolResults = [];
2126
- for (const msg of rawMessages) {
2127
- if (msg.role === "system") continue;
2128
- if (msg.role === "assistant") {
2129
- if (pendingToolResults.length > 0) {
2130
- messages.push({
2131
- role: "user",
2132
- content: pendingToolResults.map((tr) => ({
2133
- type: "tool_result",
2134
- tool_use_id: tr.tool_use_id,
2135
- content: tr.content
2136
- }))
2137
- });
2138
- pendingToolResults.length = 0;
2139
- }
2140
- const content = [];
2141
- if (msg.content && typeof msg.content === "string" && msg.content.trim()) {
2142
- content.push({ type: "text", text: msg.content });
2143
- }
2144
- const toolCalls = msg.tool_calls;
2145
- if (toolCalls && toolCalls.length > 0) {
2146
- for (const tc of toolCalls) {
2147
- let input = {};
2148
- try {
2149
- input = JSON.parse(tc.function.arguments);
2150
- } catch {
2151
- }
2152
- content.push({
2153
- type: "tool_use",
2154
- id: tc.id,
2155
- name: tc.function.name,
2156
- input
2157
- });
2158
- }
2159
- }
2160
- if (content.length > 0) {
2161
- messages.push({ role: "assistant", content });
2162
- }
2163
- } else if (msg.role === "tool") {
2164
- pendingToolResults.push({
2165
- tool_use_id: msg.tool_call_id,
2166
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
2167
- });
2168
- } else if (msg.role === "user") {
2169
- if (pendingToolResults.length > 0) {
2170
- messages.push({
2171
- role: "user",
2172
- content: pendingToolResults.map((tr) => ({
2173
- type: "tool_result",
2174
- tool_use_id: tr.tool_use_id,
2175
- content: tr.content
2176
- }))
2177
- });
2178
- pendingToolResults.length = 0;
2179
- }
2180
- if (msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0) {
2181
- const content = [];
2182
- if (msg.content && typeof msg.content === "string") {
2183
- content.push({ type: "text", text: msg.content });
2184
- }
2185
- for (const attachment of msg.attachments) {
2186
- if (attachment.type === "image") {
2187
- if (attachment.url) {
2188
- content.push({
2189
- type: "image",
2190
- source: {
2191
- type: "url",
2192
- url: attachment.url
2193
- }
2194
- });
2195
- } else if (attachment.data) {
2196
- let base64Data = attachment.data;
2197
- if (base64Data.startsWith("data:")) {
2198
- const commaIndex = base64Data.indexOf(",");
2199
- if (commaIndex !== -1) {
2200
- base64Data = base64Data.slice(commaIndex + 1);
2201
- }
2202
- }
2203
- content.push({
2204
- type: "image",
2205
- source: {
2206
- type: "base64",
2207
- media_type: attachment.mimeType || "image/png",
2208
- data: base64Data
2209
- }
2210
- });
2211
- }
2212
- } else if (attachment.type === "file" && attachment.mimeType === "application/pdf") {
2213
- if (attachment.url) {
2214
- content.push({
2215
- type: "document",
2216
- source: {
2217
- type: "url",
2218
- url: attachment.url
2219
- }
2220
- });
2221
- } else if (attachment.data) {
2222
- let base64Data = attachment.data;
2223
- if (base64Data.startsWith("data:")) {
2224
- const commaIndex = base64Data.indexOf(",");
2225
- if (commaIndex !== -1) {
2226
- base64Data = base64Data.slice(commaIndex + 1);
2227
- }
2228
- }
2229
- content.push({
2230
- type: "document",
2231
- source: {
2232
- type: "base64",
2233
- media_type: "application/pdf",
2234
- data: base64Data
2235
- }
2236
- });
2237
- }
2238
- }
2239
- }
2240
- messages.push({ role: "user", content });
2241
- } else {
2242
- messages.push({
2243
- role: "user",
2244
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
2245
- });
2246
- }
2247
- }
2248
- }
2249
- if (pendingToolResults.length > 0) {
2250
- messages.push({
2251
- role: "user",
2252
- content: pendingToolResults.map((tr) => ({
2253
- type: "tool_result",
2254
- tool_use_id: tr.tool_use_id,
2255
- content: tr.content
2256
- }))
2257
- });
2258
- }
2259
- return messages;
2260
- }
2261
- /**
2262
- * Build common request options for both streaming and non-streaming
2263
- */
2264
- buildRequestOptions(request) {
2265
- const systemMessage = request.systemPrompt || "";
2266
- let messages;
2267
- if (request.rawMessages && request.rawMessages.length > 0) {
2268
- messages = this.convertToAnthropicMessages(request.rawMessages);
2269
- } else {
2270
- const formatted = formatMessagesForAnthropic2(request.messages);
2271
- messages = formatted.messages;
2272
- }
2273
- const tools = request.actions?.map((action) => ({
2274
- name: action.name,
2275
- description: action.description,
2276
- input_schema: {
2277
- type: "object",
2278
- properties: action.parameters ? Object.fromEntries(
2279
- Object.entries(action.parameters).map(([key, param]) => [
2280
- key,
2281
- {
2282
- type: param.type,
2283
- description: param.description,
2284
- enum: param.enum
2285
- }
2286
- ])
2287
- ) : {},
2288
- required: action.parameters ? Object.entries(action.parameters).filter(([, param]) => param.required).map(([key]) => key) : []
2289
- }
2290
- }));
2291
- const options = {
2292
- model: request.config?.model || this.model,
2293
- max_tokens: request.config?.maxTokens || this.config.maxTokens || 4096,
2294
- system: systemMessage,
2295
- messages,
2296
- tools: tools?.length ? tools : void 0
2297
- };
2298
- if (this.config.thinking?.type === "enabled") {
2299
- options.thinking = {
2300
- type: "enabled",
2301
- budget_tokens: this.config.thinking.budgetTokens || 1e4
2302
- };
2303
- }
2304
- return { options, messages };
2305
- }
2306
- /**
2307
- * Non-streaming completion (for debugging/comparison with original studio-ai)
2308
- */
2309
- async complete(request) {
2310
- const client = await this.getClient();
2311
- const { options } = this.buildRequestOptions(request);
2312
- const nonStreamingOptions = {
2313
- ...options,
2314
- stream: false
2315
- };
2316
- try {
2317
- const response = await client.messages.create(nonStreamingOptions);
2318
- let content = "";
2319
- let thinking = "";
2320
- const toolCalls = [];
2321
- for (const block of response.content) {
2322
- if (block.type === "text") {
2323
- content += block.text;
2324
- } else if (block.type === "thinking") {
2325
- thinking += block.thinking;
2326
- } else if (block.type === "tool_use") {
2327
- toolCalls.push({
2328
- id: block.id,
2329
- name: block.name,
2330
- args: block.input
2331
- });
2332
- }
2333
- }
2334
- return {
2335
- content,
2336
- toolCalls,
2337
- thinking: thinking || void 0,
2338
- rawResponse: response
2339
- };
2340
- } catch (error) {
2341
- throw error;
2342
- }
2343
- }
2344
- async *stream(request) {
2345
- const client = await this.getClient();
2346
- const { options } = this.buildRequestOptions(request);
2347
- const messageId = core.generateMessageId();
2348
- yield { type: "message:start", id: messageId };
2349
- try {
2350
- const stream = await client.messages.stream(options);
2351
- let currentToolUse = null;
2352
- let isInThinkingBlock = false;
2353
- for await (const event of stream) {
2354
- if (request.signal?.aborted) {
2355
- break;
2356
- }
2357
- switch (event.type) {
2358
- case "content_block_start":
2359
- if (event.content_block.type === "tool_use") {
2360
- currentToolUse = {
2361
- id: event.content_block.id,
2362
- name: event.content_block.name,
2363
- input: ""
2364
- };
2365
- yield {
2366
- type: "action:start",
2367
- id: currentToolUse.id,
2368
- name: currentToolUse.name
2369
- };
2370
- } else if (event.content_block.type === "thinking") {
2371
- isInThinkingBlock = true;
2372
- yield { type: "thinking:start" };
2373
- }
2374
- break;
2375
- case "content_block_delta":
2376
- if (event.delta.type === "text_delta") {
2377
- yield { type: "message:delta", content: event.delta.text };
2378
- } else if (event.delta.type === "thinking_delta") {
2379
- yield { type: "thinking:delta", content: event.delta.thinking };
2380
- } else if (event.delta.type === "input_json_delta" && currentToolUse) {
2381
- currentToolUse.input += event.delta.partial_json;
2382
- }
2383
- break;
2384
- case "content_block_stop":
2385
- if (currentToolUse) {
2386
- yield {
2387
- type: "action:args",
2388
- id: currentToolUse.id,
2389
- args: currentToolUse.input
2390
- };
2391
- currentToolUse = null;
2392
- }
2393
- if (isInThinkingBlock) {
2394
- yield { type: "thinking:end" };
2395
- isInThinkingBlock = false;
2396
- }
2397
- break;
2398
- case "message_stop":
2399
- break;
2400
- }
2401
- }
2402
- yield { type: "message:end" };
2403
- yield { type: "done" };
2404
- } catch (error) {
2405
- yield {
2406
- type: "error",
2407
- message: error instanceof Error ? error.message : "Unknown error",
2408
- code: "ANTHROPIC_ERROR"
2409
- };
2410
- }
2411
- }
2412
- };
2413
- function createAnthropicAdapter(config) {
2414
- return new AnthropicAdapter(config);
2415
- }
2416
- var OllamaAdapter = class {
2417
- constructor(config = {}) {
2418
- this.provider = "ollama";
2419
- this.config = config;
2420
- this.model = config.model || "llama3";
2421
- this.baseUrl = config.baseUrl || "http://localhost:11434";
2422
- }
2423
- async *stream(request) {
2424
- const messages = formatMessages(request.messages, request.systemPrompt);
2425
- const messageId = core.generateMessageId();
2426
- yield { type: "message:start", id: messageId };
2427
- try {
2428
- const response = await fetch(`${this.baseUrl}/api/chat`, {
2429
- method: "POST",
2430
- headers: {
2431
- "Content-Type": "application/json"
2432
- },
2433
- body: JSON.stringify({
2434
- model: request.config?.model || this.model,
2435
- messages,
2436
- stream: true,
2437
- options: {
2438
- temperature: request.config?.temperature ?? this.config.temperature,
2439
- num_predict: request.config?.maxTokens ?? this.config.maxTokens
2440
- }
2441
- }),
2442
- signal: request.signal
2443
- });
2444
- if (!response.ok) {
2445
- throw new Error(`Ollama API error: ${response.status}`);
2446
- }
2447
- if (!response.body) {
2448
- throw new Error("No response body");
2449
- }
2450
- const reader = response.body.getReader();
2451
- const decoder = new TextDecoder();
2452
- let buffer = "";
2453
- while (true) {
2454
- const { done, value } = await reader.read();
2455
- if (done) break;
2456
- buffer += decoder.decode(value, { stream: true });
2457
- const lines = buffer.split("\n");
2458
- buffer = lines.pop() || "";
2459
- for (const line of lines) {
2460
- if (!line.trim()) continue;
2461
- try {
2462
- const chunk = JSON.parse(line);
2463
- if (chunk.message?.content) {
2464
- yield { type: "message:delta", content: chunk.message.content };
2465
- }
2466
- if (chunk.done) {
2467
- break;
2468
- }
2469
- } catch {
2470
- }
2471
- }
2472
- }
2473
- yield { type: "message:end" };
2474
- yield { type: "done" };
2475
- } catch (error) {
2476
- if (error.name === "AbortError") {
2477
- yield { type: "done" };
2478
- } else {
2479
- yield {
2480
- type: "error",
2481
- message: error instanceof Error ? error.message : "Unknown error",
2482
- code: "OLLAMA_ERROR"
2483
- };
2484
- }
2485
- }
2486
- }
2487
- };
2488
- function createOllamaAdapter(config) {
2489
- return new OllamaAdapter(config);
2490
- }
2491
- function attachmentToGeminiPart(attachment) {
2492
- if (!attachment.data) {
2493
- console.warn(
2494
- "Gemini adapter: URL-based attachments not supported, skipping"
2495
- );
2496
- return null;
2497
- }
2498
- if (attachment.type === "image") {
2499
- let base64Data = attachment.data;
2500
- if (base64Data.startsWith("data:")) {
2501
- const commaIndex = base64Data.indexOf(",");
2502
- if (commaIndex !== -1) {
2503
- base64Data = base64Data.slice(commaIndex + 1);
2504
- }
2505
- }
2506
- return {
2507
- inlineData: {
2508
- mimeType: attachment.mimeType || "image/png",
2509
- data: base64Data
2510
- }
2511
- };
2512
- }
2513
- if (attachment.type === "audio" || attachment.type === "video") {
2514
- let base64Data = attachment.data;
2515
- if (base64Data.startsWith("data:")) {
2516
- const commaIndex = base64Data.indexOf(",");
2517
- if (commaIndex !== -1) {
2518
- base64Data = base64Data.slice(commaIndex + 1);
2519
- }
2520
- }
2521
- return {
2522
- inlineData: {
2523
- mimeType: attachment.mimeType || (attachment.type === "audio" ? "audio/mp3" : "video/mp4"),
2524
- data: base64Data
2525
- }
2526
- };
542
+ // src/adapters/base.ts
543
+ function formatMessages(messages, systemPrompt) {
544
+ const formatted = [];
545
+ if (systemPrompt) {
546
+ formatted.push({ role: "system", content: systemPrompt });
2527
547
  }
2528
- return null;
2529
- }
2530
- function messageToGeminiContent(msg) {
2531
- if (msg.role === "system") return null;
2532
- const parts = [];
2533
- if (msg.role === "tool" && msg.tool_call_id) {
2534
- let responseData;
2535
- try {
2536
- responseData = JSON.parse(msg.content || "{}");
2537
- } catch {
2538
- responseData = { result: msg.content || "" };
2539
- }
2540
- const toolName = msg.metadata?.toolName || "tool";
2541
- parts.push({
2542
- functionResponse: {
2543
- name: toolName,
2544
- response: responseData
2545
- }
548
+ for (const msg of messages) {
549
+ formatted.push({
550
+ role: msg.role,
551
+ content: msg.content ?? ""
2546
552
  });
2547
- return { role: "user", parts };
2548
553
  }
2549
- if (msg.content) {
2550
- parts.push({ text: msg.content });
554
+ return formatted;
555
+ }
556
+ function parameterToJsonSchema(param) {
557
+ const schema = {
558
+ type: param.type
559
+ };
560
+ if (param.description) {
561
+ schema.description = param.description;
2551
562
  }
2552
- const attachments = msg.metadata?.attachments;
2553
- if (attachments && Array.isArray(attachments)) {
2554
- for (const attachment of attachments) {
2555
- const part = attachmentToGeminiPart(attachment);
2556
- if (part) {
2557
- parts.push(part);
2558
- }
2559
- }
563
+ if (param.enum) {
564
+ schema.enum = param.enum;
2560
565
  }
2561
- if (msg.role === "assistant" && msg.tool_calls && msg.tool_calls.length > 0) {
2562
- for (const tc of msg.tool_calls) {
2563
- let args = {};
2564
- try {
2565
- args = JSON.parse(tc.function.arguments);
2566
- } catch {
2567
- }
2568
- parts.push({
2569
- functionCall: {
2570
- name: tc.function.name,
2571
- args
2572
- }
2573
- });
2574
- }
566
+ if (param.type === "array" && param.items) {
567
+ schema.items = parameterToJsonSchema(
568
+ param.items
569
+ );
2575
570
  }
2576
- if (parts.length === 0) return null;
2577
- return {
2578
- role: msg.role === "assistant" ? "model" : "user",
2579
- parts
2580
- };
571
+ if (param.type === "object" && param.properties) {
572
+ schema.properties = Object.fromEntries(
573
+ Object.entries(param.properties).map(([key, prop]) => [
574
+ key,
575
+ parameterToJsonSchema(
576
+ prop
577
+ )
578
+ ])
579
+ );
580
+ }
581
+ return schema;
2581
582
  }
2582
- function formatToolsForGemini(actions) {
2583
- if (!actions || actions.length === 0) return void 0;
2584
- return {
2585
- functionDeclarations: actions.map((action) => ({
583
+ function formatTools(actions) {
584
+ return actions.map((action) => ({
585
+ type: "function",
586
+ function: {
2586
587
  name: action.name,
2587
588
  description: action.description,
2588
- parameters: action.parameters ? {
589
+ parameters: {
2589
590
  type: "object",
2590
- properties: Object.fromEntries(
591
+ properties: action.parameters ? Object.fromEntries(
2591
592
  Object.entries(action.parameters).map(([key, param]) => [
2592
593
  key,
2593
- {
2594
- type: param.type,
2595
- description: param.description,
2596
- enum: param.enum
2597
- }
594
+ parameterToJsonSchema(param)
2598
595
  ])
2599
- ),
2600
- required: Object.entries(action.parameters).filter(([, param]) => param.required).map(([key]) => key)
2601
- } : void 0
2602
- }))
2603
- };
596
+ ) : {},
597
+ required: action.parameters ? Object.entries(action.parameters).filter(([, param]) => param.required).map(([key]) => key) : []
598
+ }
599
+ }
600
+ }));
2604
601
  }
2605
- var GoogleAdapter = class {
2606
- constructor(config) {
2607
- this.provider = "google";
2608
- this.config = config;
2609
- this.model = config.model || "gemini-2.0-flash";
602
+ function hasImageAttachments(message) {
603
+ const attachments = message.metadata?.attachments;
604
+ return attachments?.some((a) => a.type === "image") ?? false;
605
+ }
606
+ function hasMediaAttachments(message) {
607
+ const attachments = message.metadata?.attachments;
608
+ return attachments?.some(
609
+ (a) => a.type === "image" || a.type === "file" && a.mimeType === "application/pdf"
610
+ ) ?? false;
611
+ }
612
+ function attachmentToAnthropicImage(attachment) {
613
+ if (attachment.type !== "image") return null;
614
+ if (attachment.url) {
615
+ return {
616
+ type: "image",
617
+ source: {
618
+ type: "url",
619
+ url: attachment.url
620
+ }
621
+ };
2610
622
  }
2611
- async getClient() {
2612
- if (!this.client) {
2613
- const { GoogleGenerativeAI } = await import('@google/generative-ai');
2614
- this.client = new GoogleGenerativeAI(this.config.apiKey);
623
+ if (!attachment.data) return null;
624
+ let base64Data = attachment.data;
625
+ if (base64Data.startsWith("data:")) {
626
+ const commaIndex = base64Data.indexOf(",");
627
+ if (commaIndex !== -1) {
628
+ base64Data = base64Data.slice(commaIndex + 1);
2615
629
  }
2616
- return this.client;
2617
630
  }
2618
- async *stream(request) {
2619
- const client = await this.getClient();
2620
- const modelId = request.config?.model || this.model;
2621
- const model = client.getGenerativeModel({
2622
- model: modelId,
2623
- safetySettings: this.config.safetySettings
2624
- });
2625
- let contents = [];
2626
- let systemInstruction;
2627
- if (request.rawMessages && request.rawMessages.length > 0) {
2628
- for (const msg of request.rawMessages) {
2629
- if (msg.role === "system") {
2630
- systemInstruction = (systemInstruction || "") + (msg.content || "");
2631
- continue;
2632
- }
2633
- const content = messageToGeminiContent(msg);
2634
- if (content) {
2635
- contents.push(content);
2636
- }
2637
- }
2638
- if (request.systemPrompt && !systemInstruction) {
2639
- systemInstruction = request.systemPrompt;
2640
- }
2641
- } else {
2642
- for (const msg of request.messages) {
2643
- if (msg.role === "system") {
2644
- systemInstruction = (systemInstruction || "") + (msg.content || "");
2645
- continue;
2646
- }
2647
- const content = messageToGeminiContent(msg);
2648
- if (content) {
2649
- contents.push(content);
2650
- }
2651
- }
2652
- if (request.systemPrompt) {
2653
- systemInstruction = request.systemPrompt;
2654
- }
631
+ return {
632
+ type: "image",
633
+ source: {
634
+ type: "base64",
635
+ media_type: attachment.mimeType || "image/png",
636
+ data: base64Data
2655
637
  }
2656
- if (contents.length === 0 || contents[0].role !== "user") {
2657
- contents = [{ role: "user", parts: [{ text: "" }] }, ...contents];
638
+ };
639
+ }
640
+ function attachmentToOpenAIImage(attachment) {
641
+ if (attachment.type !== "image") return null;
642
+ let imageUrl;
643
+ if (attachment.url) {
644
+ imageUrl = attachment.url;
645
+ } else if (attachment.data) {
646
+ imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
647
+ } else {
648
+ return null;
649
+ }
650
+ return {
651
+ type: "image_url",
652
+ image_url: {
653
+ url: imageUrl,
654
+ detail: "auto"
2658
655
  }
2659
- const mergedContents = [];
2660
- for (const content of contents) {
2661
- const last = mergedContents[mergedContents.length - 1];
2662
- if (last && last.role === content.role) {
2663
- last.parts.push(...content.parts);
2664
- } else {
2665
- mergedContents.push({ ...content, parts: [...content.parts] });
656
+ };
657
+ }
658
+ function attachmentToAnthropicDocument(attachment) {
659
+ if (attachment.type !== "file" || attachment.mimeType !== "application/pdf") {
660
+ return null;
661
+ }
662
+ if (attachment.url) {
663
+ return {
664
+ type: "document",
665
+ source: {
666
+ type: "url",
667
+ url: attachment.url
2666
668
  }
669
+ };
670
+ }
671
+ if (!attachment.data) return null;
672
+ let base64Data = attachment.data;
673
+ if (base64Data.startsWith("data:")) {
674
+ const commaIndex = base64Data.indexOf(",");
675
+ if (commaIndex !== -1) {
676
+ base64Data = base64Data.slice(commaIndex + 1);
2667
677
  }
2668
- const tools = formatToolsForGemini(request.actions);
2669
- const messageId = core.generateMessageId();
2670
- yield { type: "message:start", id: messageId };
2671
- try {
2672
- const chat = model.startChat({
2673
- history: mergedContents.slice(0, -1),
2674
- // All but the last message
2675
- systemInstruction: systemInstruction ? { parts: [{ text: systemInstruction }] } : void 0,
2676
- tools: tools ? [tools] : void 0,
2677
- generationConfig: {
2678
- temperature: request.config?.temperature ?? this.config.temperature,
2679
- maxOutputTokens: request.config?.maxTokens ?? this.config.maxTokens
2680
- }
2681
- });
2682
- const lastMessage = mergedContents[mergedContents.length - 1];
2683
- const result = await chat.sendMessageStream(lastMessage.parts);
2684
- let currentToolCall = null;
2685
- for await (const chunk of result.stream) {
2686
- if (request.signal?.aborted) {
2687
- break;
2688
- }
2689
- const candidate = chunk.candidates?.[0];
2690
- if (!candidate?.content?.parts) continue;
2691
- for (const part of candidate.content.parts) {
2692
- if ("text" in part && part.text) {
2693
- yield { type: "message:delta", content: part.text };
2694
- }
2695
- if ("functionCall" in part && part.functionCall) {
2696
- const fc = part.functionCall;
2697
- const toolId = core.generateToolCallId();
2698
- if (currentToolCall) {
2699
- yield {
2700
- type: "action:args",
2701
- id: currentToolCall.id,
2702
- args: JSON.stringify(currentToolCall.args)
2703
- };
2704
- }
2705
- currentToolCall = {
2706
- id: toolId,
2707
- name: fc.name,
2708
- args: fc.args || {}
2709
- };
2710
- yield {
2711
- type: "action:start",
2712
- id: toolId,
2713
- name: fc.name
2714
- };
2715
- }
2716
- }
2717
- if (candidate.finishReason) {
2718
- if (currentToolCall) {
2719
- yield {
2720
- type: "action:args",
2721
- id: currentToolCall.id,
2722
- args: JSON.stringify(currentToolCall.args)
2723
- };
2724
- }
2725
- }
2726
- }
2727
- yield { type: "message:end" };
2728
- yield { type: "done" };
2729
- } catch (error) {
2730
- yield {
2731
- type: "error",
2732
- message: error instanceof Error ? error.message : "Unknown error",
2733
- code: "GOOGLE_ERROR"
2734
- };
678
+ }
679
+ return {
680
+ type: "document",
681
+ source: {
682
+ type: "base64",
683
+ media_type: "application/pdf",
684
+ data: base64Data
2735
685
  }
686
+ };
687
+ }
688
+ function messageToAnthropicContent(message) {
689
+ const attachments = message.metadata?.attachments;
690
+ const content = message.content ?? "";
691
+ if (!hasMediaAttachments(message)) {
692
+ return content;
2736
693
  }
2737
- /**
2738
- * Non-streaming completion (optional, for debugging)
2739
- */
2740
- async complete(request) {
2741
- const client = await this.getClient();
2742
- const modelId = request.config?.model || this.model;
2743
- const model = client.getGenerativeModel({
2744
- model: modelId,
2745
- safetySettings: this.config.safetySettings
2746
- });
2747
- let contents = [];
2748
- let systemInstruction;
2749
- for (const msg of request.messages) {
2750
- if (msg.role === "system") {
2751
- systemInstruction = (systemInstruction || "") + (msg.content || "");
694
+ const blocks = [];
695
+ if (attachments) {
696
+ for (const attachment of attachments) {
697
+ const imageBlock = attachmentToAnthropicImage(attachment);
698
+ if (imageBlock) {
699
+ blocks.push(imageBlock);
2752
700
  continue;
2753
701
  }
2754
- const content = messageToGeminiContent(msg);
2755
- if (content) {
2756
- contents.push(content);
702
+ const docBlock = attachmentToAnthropicDocument(attachment);
703
+ if (docBlock) {
704
+ blocks.push(docBlock);
2757
705
  }
2758
706
  }
2759
- if (request.systemPrompt) {
2760
- systemInstruction = request.systemPrompt;
2761
- }
2762
- if (contents.length === 0 || contents[0].role !== "user") {
2763
- contents = [{ role: "user", parts: [{ text: "" }] }, ...contents];
707
+ }
708
+ if (content) {
709
+ blocks.push({ type: "text", text: content });
710
+ }
711
+ return blocks;
712
+ }
713
+ function messageToOpenAIContent(message) {
714
+ const attachments = message.metadata?.attachments;
715
+ const content = message.content ?? "";
716
+ if (!hasImageAttachments(message)) {
717
+ return content;
718
+ }
719
+ const blocks = [];
720
+ if (content) {
721
+ blocks.push({ type: "text", text: content });
722
+ }
723
+ if (attachments) {
724
+ for (const attachment of attachments) {
725
+ const imageBlock = attachmentToOpenAIImage(attachment);
726
+ if (imageBlock) {
727
+ blocks.push(imageBlock);
728
+ }
2764
729
  }
2765
- const mergedContents = [];
2766
- for (const content of contents) {
2767
- const last = mergedContents[mergedContents.length - 1];
2768
- if (last && last.role === content.role) {
2769
- last.parts.push(...content.parts);
2770
- } else {
2771
- mergedContents.push({ ...content, parts: [...content.parts] });
730
+ }
731
+ return blocks;
732
+ }
733
+ function formatMessagesForAnthropic(messages, systemPrompt) {
734
+ const formatted = [];
735
+ for (let i = 0; i < messages.length; i++) {
736
+ const msg = messages[i];
737
+ if (msg.role === "system") continue;
738
+ if (msg.role === "assistant") {
739
+ const content = [];
740
+ if (msg.content) {
741
+ content.push({ type: "text", text: msg.content });
2772
742
  }
2773
- }
2774
- const tools = formatToolsForGemini(request.actions);
2775
- const chat = model.startChat({
2776
- history: mergedContents.slice(0, -1),
2777
- systemInstruction: systemInstruction ? { parts: [{ text: systemInstruction }] } : void 0,
2778
- tools: tools ? [tools] : void 0,
2779
- generationConfig: {
2780
- temperature: request.config?.temperature ?? this.config.temperature,
2781
- maxOutputTokens: request.config?.maxTokens ?? this.config.maxTokens
743
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
744
+ for (const tc of msg.tool_calls) {
745
+ content.push({
746
+ type: "tool_use",
747
+ id: tc.id,
748
+ name: tc.function.name,
749
+ input: JSON.parse(tc.function.arguments)
750
+ });
751
+ }
2782
752
  }
2783
- });
2784
- const lastMessage = mergedContents[mergedContents.length - 1];
2785
- const result = await chat.sendMessage(lastMessage.parts);
2786
- const response = result.response;
2787
- let textContent = "";
2788
- const toolCalls = [];
2789
- const candidate = response.candidates?.[0];
2790
- if (candidate?.content?.parts) {
2791
- for (const part of candidate.content.parts) {
2792
- if ("text" in part && part.text) {
2793
- textContent += part.text;
753
+ formatted.push({
754
+ role: "assistant",
755
+ content: content.length === 1 && content[0].type === "text" ? content[0].text : content
756
+ });
757
+ } else if (msg.role === "tool" && msg.tool_call_id) {
758
+ const toolResults = [
759
+ {
760
+ type: "tool_result",
761
+ tool_use_id: msg.tool_call_id,
762
+ content: msg.content ?? ""
2794
763
  }
2795
- if ("functionCall" in part && part.functionCall) {
2796
- toolCalls.push({
2797
- id: core.generateToolCallId(),
2798
- name: part.functionCall.name,
2799
- args: part.functionCall.args || {}
764
+ ];
765
+ while (i + 1 < messages.length && messages[i + 1].role === "tool") {
766
+ i++;
767
+ const nextTool = messages[i];
768
+ if (nextTool.tool_call_id) {
769
+ toolResults.push({
770
+ type: "tool_result",
771
+ tool_use_id: nextTool.tool_call_id,
772
+ content: nextTool.content ?? ""
2800
773
  });
2801
774
  }
2802
775
  }
776
+ formatted.push({
777
+ role: "user",
778
+ content: toolResults
779
+ });
780
+ } else if (msg.role === "user") {
781
+ formatted.push({
782
+ role: "user",
783
+ content: messageToAnthropicContent(msg)
784
+ });
2803
785
  }
2804
- return {
2805
- content: textContent,
2806
- toolCalls,
2807
- rawResponse: response
2808
- };
2809
786
  }
2810
- };
2811
- function createGoogleAdapter(config) {
2812
- return new GoogleAdapter(config);
787
+ return {
788
+ system: "",
789
+ messages: formatted
790
+ };
791
+ }
792
+ function formatMessagesForOpenAI(messages, systemPrompt) {
793
+ const formatted = [];
794
+ if (systemPrompt) {
795
+ formatted.push({ role: "system", content: systemPrompt });
796
+ }
797
+ for (const msg of messages) {
798
+ if (msg.role === "system") {
799
+ formatted.push({ role: "system", content: msg.content ?? "" });
800
+ } else if (msg.role === "user") {
801
+ formatted.push({
802
+ role: "user",
803
+ content: messageToOpenAIContent(msg)
804
+ });
805
+ } else if (msg.role === "assistant") {
806
+ const assistantMsg = {
807
+ role: "assistant",
808
+ content: msg.content
809
+ };
810
+ if (msg.tool_calls && msg.tool_calls.length > 0) {
811
+ assistantMsg.tool_calls = msg.tool_calls;
812
+ }
813
+ formatted.push(assistantMsg);
814
+ } else if (msg.role === "tool" && msg.tool_call_id) {
815
+ formatted.push({
816
+ role: "tool",
817
+ content: msg.content ?? "",
818
+ tool_call_id: msg.tool_call_id
819
+ });
820
+ }
821
+ }
822
+ return formatted;
2813
823
  }
2814
- var XAI_BASE_URL = "https://api.x.ai/v1";
2815
- var XAIAdapter = class {
824
+
825
+ // src/adapters/openai.ts
826
+ var OpenAIAdapter = class {
2816
827
  constructor(config) {
2817
- this.provider = "xai";
828
+ this.provider = "openai";
2818
829
  this.config = config;
2819
- this.model = config.model || "grok-2";
830
+ this.model = config.model || "gpt-4o";
2820
831
  }
2821
832
  async getClient() {
2822
833
  if (!this.client) {
2823
834
  const { default: OpenAI } = await import('openai');
2824
835
  this.client = new OpenAI({
2825
836
  apiKey: this.config.apiKey,
2826
- baseURL: this.config.baseUrl || XAI_BASE_URL
837
+ baseURL: this.config.baseUrl
2827
838
  });
2828
839
  }
2829
840
  return this.client;
@@ -2841,9 +852,13 @@ var XAIAdapter = class {
2841
852
  }
2842
853
  for (const attachment of msg.attachments) {
2843
854
  if (attachment.type === "image") {
2844
- let imageUrl = attachment.data;
2845
- if (!imageUrl.startsWith("data:")) {
2846
- imageUrl = `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
855
+ let imageUrl;
856
+ if (attachment.url) {
857
+ imageUrl = attachment.url;
858
+ } else if (attachment.data) {
859
+ imageUrl = attachment.data.startsWith("data:") ? attachment.data : `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
860
+ } else {
861
+ continue;
2847
862
  }
2848
863
  content.push({
2849
864
  type: "image_url",
@@ -2869,7 +884,7 @@ var XAIAdapter = class {
2869
884
  messages = processedMessages;
2870
885
  }
2871
886
  } else {
2872
- messages = formatMessagesForOpenAI2(
887
+ messages = formatMessagesForOpenAI(
2873
888
  request.messages,
2874
889
  request.systemPrompt
2875
890
  );
@@ -2905,216 +920,348 @@ var XAIAdapter = class {
2905
920
  args: currentToolCall.arguments
2906
921
  };
2907
922
  }
2908
- currentToolCall = {
2909
- id: toolCall.id,
2910
- name: toolCall.function?.name || "",
2911
- arguments: toolCall.function?.arguments || ""
2912
- };
2913
- yield {
2914
- type: "action:start",
2915
- id: currentToolCall.id,
2916
- name: currentToolCall.name
2917
- };
2918
- } else if (currentToolCall && toolCall.function?.arguments) {
2919
- currentToolCall.arguments += toolCall.function.arguments;
923
+ currentToolCall = {
924
+ id: toolCall.id,
925
+ name: toolCall.function?.name || "",
926
+ arguments: toolCall.function?.arguments || ""
927
+ };
928
+ yield {
929
+ type: "action:start",
930
+ id: currentToolCall.id,
931
+ name: currentToolCall.name
932
+ };
933
+ } else if (currentToolCall && toolCall.function?.arguments) {
934
+ currentToolCall.arguments += toolCall.function.arguments;
935
+ }
936
+ }
937
+ }
938
+ if (chunk.choices[0]?.finish_reason) {
939
+ if (currentToolCall) {
940
+ yield {
941
+ type: "action:args",
942
+ id: currentToolCall.id,
943
+ args: currentToolCall.arguments
944
+ };
945
+ }
946
+ }
947
+ }
948
+ yield { type: "message:end" };
949
+ yield { type: "done" };
950
+ } catch (error) {
951
+ yield {
952
+ type: "error",
953
+ message: error instanceof Error ? error.message : "Unknown error",
954
+ code: "OPENAI_ERROR"
955
+ };
956
+ }
957
+ }
958
+ };
959
+ function createOpenAIAdapter(config) {
960
+ return new OpenAIAdapter(config);
961
+ }
962
+ var AnthropicAdapter = class {
963
+ constructor(config) {
964
+ this.provider = "anthropic";
965
+ this.config = config;
966
+ this.model = config.model || "claude-3-5-sonnet-latest";
967
+ }
968
+ async getClient() {
969
+ if (!this.client) {
970
+ const { default: Anthropic } = await import('@anthropic-ai/sdk');
971
+ this.client = new Anthropic({
972
+ apiKey: this.config.apiKey
973
+ });
974
+ }
975
+ return this.client;
976
+ }
977
+ /**
978
+ * Convert OpenAI-style messages to Anthropic format
979
+ *
980
+ * OpenAI format:
981
+ * - { role: "assistant", content: "...", tool_calls: [...] }
982
+ * - { role: "tool", tool_call_id: "...", content: "..." }
983
+ *
984
+ * Anthropic format:
985
+ * - { role: "assistant", content: [{ type: "text", text: "..." }, { type: "tool_use", id: "...", name: "...", input: {...} }] }
986
+ * - { role: "user", content: [{ type: "tool_result", tool_use_id: "...", content: "..." }] }
987
+ */
988
+ convertToAnthropicMessages(rawMessages) {
989
+ const messages = [];
990
+ const pendingToolResults = [];
991
+ for (const msg of rawMessages) {
992
+ if (msg.role === "system") continue;
993
+ if (msg.role === "assistant") {
994
+ if (pendingToolResults.length > 0) {
995
+ messages.push({
996
+ role: "user",
997
+ content: pendingToolResults.map((tr) => ({
998
+ type: "tool_result",
999
+ tool_use_id: tr.tool_use_id,
1000
+ content: tr.content
1001
+ }))
1002
+ });
1003
+ pendingToolResults.length = 0;
1004
+ }
1005
+ const content = [];
1006
+ if (msg.content && typeof msg.content === "string" && msg.content.trim()) {
1007
+ content.push({ type: "text", text: msg.content });
1008
+ }
1009
+ const toolCalls = msg.tool_calls;
1010
+ if (toolCalls && toolCalls.length > 0) {
1011
+ for (const tc of toolCalls) {
1012
+ let input = {};
1013
+ try {
1014
+ input = JSON.parse(tc.function.arguments);
1015
+ } catch {
1016
+ }
1017
+ content.push({
1018
+ type: "tool_use",
1019
+ id: tc.id,
1020
+ name: tc.function.name,
1021
+ input
1022
+ });
1023
+ }
1024
+ }
1025
+ if (content.length > 0) {
1026
+ messages.push({ role: "assistant", content });
1027
+ }
1028
+ } else if (msg.role === "tool") {
1029
+ pendingToolResults.push({
1030
+ tool_use_id: msg.tool_call_id,
1031
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
1032
+ });
1033
+ } else if (msg.role === "user") {
1034
+ if (pendingToolResults.length > 0) {
1035
+ messages.push({
1036
+ role: "user",
1037
+ content: pendingToolResults.map((tr) => ({
1038
+ type: "tool_result",
1039
+ tool_use_id: tr.tool_use_id,
1040
+ content: tr.content
1041
+ }))
1042
+ });
1043
+ pendingToolResults.length = 0;
1044
+ }
1045
+ if (msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0) {
1046
+ const content = [];
1047
+ if (msg.content && typeof msg.content === "string") {
1048
+ content.push({ type: "text", text: msg.content });
1049
+ }
1050
+ for (const attachment of msg.attachments) {
1051
+ if (attachment.type === "image") {
1052
+ if (attachment.url) {
1053
+ content.push({
1054
+ type: "image",
1055
+ source: {
1056
+ type: "url",
1057
+ url: attachment.url
1058
+ }
1059
+ });
1060
+ } else if (attachment.data) {
1061
+ let base64Data = attachment.data;
1062
+ if (base64Data.startsWith("data:")) {
1063
+ const commaIndex = base64Data.indexOf(",");
1064
+ if (commaIndex !== -1) {
1065
+ base64Data = base64Data.slice(commaIndex + 1);
1066
+ }
1067
+ }
1068
+ content.push({
1069
+ type: "image",
1070
+ source: {
1071
+ type: "base64",
1072
+ media_type: attachment.mimeType || "image/png",
1073
+ data: base64Data
1074
+ }
1075
+ });
1076
+ }
1077
+ } else if (attachment.type === "file" && attachment.mimeType === "application/pdf") {
1078
+ if (attachment.url) {
1079
+ content.push({
1080
+ type: "document",
1081
+ source: {
1082
+ type: "url",
1083
+ url: attachment.url
1084
+ }
1085
+ });
1086
+ } else if (attachment.data) {
1087
+ let base64Data = attachment.data;
1088
+ if (base64Data.startsWith("data:")) {
1089
+ const commaIndex = base64Data.indexOf(",");
1090
+ if (commaIndex !== -1) {
1091
+ base64Data = base64Data.slice(commaIndex + 1);
1092
+ }
1093
+ }
1094
+ content.push({
1095
+ type: "document",
1096
+ source: {
1097
+ type: "base64",
1098
+ media_type: "application/pdf",
1099
+ data: base64Data
1100
+ }
1101
+ });
1102
+ }
2920
1103
  }
2921
1104
  }
2922
- }
2923
- if (chunk.choices[0]?.finish_reason) {
2924
- if (currentToolCall) {
2925
- yield {
2926
- type: "action:args",
2927
- id: currentToolCall.id,
2928
- args: currentToolCall.arguments
2929
- };
2930
- }
1105
+ messages.push({ role: "user", content });
1106
+ } else {
1107
+ messages.push({
1108
+ role: "user",
1109
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
1110
+ });
2931
1111
  }
2932
1112
  }
2933
- yield { type: "message:end" };
2934
- yield { type: "done" };
2935
- } catch (error) {
2936
- yield {
2937
- type: "error",
2938
- message: error instanceof Error ? error.message : "Unknown error",
2939
- code: "XAI_ERROR"
2940
- };
2941
1113
  }
1114
+ if (pendingToolResults.length > 0) {
1115
+ messages.push({
1116
+ role: "user",
1117
+ content: pendingToolResults.map((tr) => ({
1118
+ type: "tool_result",
1119
+ tool_use_id: tr.tool_use_id,
1120
+ content: tr.content
1121
+ }))
1122
+ });
1123
+ }
1124
+ return messages;
2942
1125
  }
2943
1126
  /**
2944
- * Non-streaming completion (optional, for debugging)
1127
+ * Build common request options for both streaming and non-streaming
2945
1128
  */
2946
- async complete(request) {
2947
- const client = await this.getClient();
1129
+ buildRequestOptions(request) {
1130
+ const systemMessage = request.systemPrompt || "";
2948
1131
  let messages;
2949
1132
  if (request.rawMessages && request.rawMessages.length > 0) {
2950
- messages = request.rawMessages;
2951
- if (request.systemPrompt) {
2952
- const hasSystem = messages.some((m) => m.role === "system");
2953
- if (!hasSystem) {
2954
- messages = [
2955
- { role: "system", content: request.systemPrompt },
2956
- ...messages
2957
- ];
2958
- }
2959
- }
1133
+ messages = this.convertToAnthropicMessages(request.rawMessages);
2960
1134
  } else {
2961
- messages = formatMessagesForOpenAI2(
2962
- request.messages,
2963
- request.systemPrompt
2964
- );
1135
+ const formatted = formatMessagesForAnthropic(request.messages);
1136
+ messages = formatted.messages;
2965
1137
  }
2966
- const tools = request.actions?.length ? formatTools(request.actions) : void 0;
2967
- const response = await client.chat.completions.create({
1138
+ const tools = request.actions?.map((action) => ({
1139
+ name: action.name,
1140
+ description: action.description,
1141
+ input_schema: {
1142
+ type: "object",
1143
+ properties: action.parameters ? Object.fromEntries(
1144
+ Object.entries(action.parameters).map(([key, param]) => [
1145
+ key,
1146
+ {
1147
+ type: param.type,
1148
+ description: param.description,
1149
+ enum: param.enum
1150
+ }
1151
+ ])
1152
+ ) : {},
1153
+ required: action.parameters ? Object.entries(action.parameters).filter(([, param]) => param.required).map(([key]) => key) : []
1154
+ }
1155
+ }));
1156
+ const options = {
2968
1157
  model: request.config?.model || this.model,
1158
+ max_tokens: request.config?.maxTokens || this.config.maxTokens || 4096,
1159
+ system: systemMessage,
2969
1160
  messages,
2970
- tools,
2971
- temperature: request.config?.temperature ?? this.config.temperature,
2972
- max_tokens: request.config?.maxTokens ?? this.config.maxTokens
2973
- });
2974
- const choice = response.choices[0];
2975
- const message = choice?.message;
2976
- const toolCalls = (message?.tool_calls || []).map((tc) => ({
2977
- id: tc.id,
2978
- name: tc.function.name,
2979
- args: JSON.parse(tc.function.arguments || "{}")
2980
- }));
2981
- return {
2982
- content: message?.content || "",
2983
- toolCalls,
2984
- rawResponse: response
1161
+ tools: tools?.length ? tools : void 0
2985
1162
  };
2986
- }
2987
- };
2988
- function createXAIAdapter(config) {
2989
- return new XAIAdapter(config);
2990
- }
2991
- var DEFAULT_API_VERSION = "2024-08-01-preview";
2992
- function buildAzureEndpoint(resourceName, deploymentName, apiVersion) {
2993
- return `https://${resourceName}.openai.azure.com/openai/deployments/${deploymentName}`;
2994
- }
2995
- var AzureAdapter = class {
2996
- constructor(config) {
2997
- this.provider = "azure";
2998
- this.config = config;
2999
- this.model = config.deploymentName;
3000
- }
3001
- async getClient() {
3002
- if (!this.client) {
3003
- const { AzureOpenAI } = await import('openai');
3004
- const apiVersion = this.config.apiVersion || DEFAULT_API_VERSION;
3005
- const endpoint = this.config.baseUrl || buildAzureEndpoint(
3006
- this.config.resourceName,
3007
- this.config.deploymentName);
3008
- this.client = new AzureOpenAI({
3009
- apiKey: this.config.apiKey,
3010
- endpoint,
3011
- apiVersion,
3012
- deployment: this.config.deploymentName
3013
- });
1163
+ if (this.config.thinking?.type === "enabled") {
1164
+ options.thinking = {
1165
+ type: "enabled",
1166
+ budget_tokens: this.config.thinking.budgetTokens || 1e4
1167
+ };
3014
1168
  }
3015
- return this.client;
1169
+ return { options, messages };
3016
1170
  }
3017
- async *stream(request) {
1171
+ /**
1172
+ * Non-streaming completion (for debugging/comparison with original studio-ai)
1173
+ */
1174
+ async complete(request) {
3018
1175
  const client = await this.getClient();
3019
- let messages;
3020
- if (request.rawMessages && request.rawMessages.length > 0) {
3021
- const processedMessages = request.rawMessages.map((msg) => {
3022
- const hasAttachments = msg.attachments && Array.isArray(msg.attachments) && msg.attachments.length > 0;
3023
- if (hasAttachments) {
3024
- const content = [];
3025
- if (msg.content) {
3026
- content.push({ type: "text", text: msg.content });
3027
- }
3028
- for (const attachment of msg.attachments) {
3029
- if (attachment.type === "image") {
3030
- let imageUrl = attachment.data;
3031
- if (!imageUrl.startsWith("data:")) {
3032
- imageUrl = `data:${attachment.mimeType || "image/png"};base64,${attachment.data}`;
3033
- }
3034
- content.push({
3035
- type: "image_url",
3036
- image_url: { url: imageUrl, detail: "auto" }
3037
- });
3038
- }
3039
- }
3040
- return { ...msg, content, attachments: void 0 };
3041
- }
3042
- return msg;
3043
- });
3044
- if (request.systemPrompt) {
3045
- const hasSystem = processedMessages.some((m) => m.role === "system");
3046
- if (!hasSystem) {
3047
- messages = [
3048
- { role: "system", content: request.systemPrompt },
3049
- ...processedMessages
3050
- ];
3051
- } else {
3052
- messages = processedMessages;
1176
+ const { options } = this.buildRequestOptions(request);
1177
+ const nonStreamingOptions = {
1178
+ ...options,
1179
+ stream: false
1180
+ };
1181
+ try {
1182
+ const response = await client.messages.create(nonStreamingOptions);
1183
+ let content = "";
1184
+ let thinking = "";
1185
+ const toolCalls = [];
1186
+ for (const block of response.content) {
1187
+ if (block.type === "text") {
1188
+ content += block.text;
1189
+ } else if (block.type === "thinking") {
1190
+ thinking += block.thinking;
1191
+ } else if (block.type === "tool_use") {
1192
+ toolCalls.push({
1193
+ id: block.id,
1194
+ name: block.name,
1195
+ args: block.input
1196
+ });
3053
1197
  }
3054
- } else {
3055
- messages = processedMessages;
3056
1198
  }
3057
- } else {
3058
- messages = formatMessagesForOpenAI2(
3059
- request.messages,
3060
- request.systemPrompt
3061
- );
1199
+ return {
1200
+ content,
1201
+ toolCalls,
1202
+ thinking: thinking || void 0,
1203
+ rawResponse: response
1204
+ };
1205
+ } catch (error) {
1206
+ throw error;
3062
1207
  }
3063
- const tools = request.actions?.length ? formatTools(request.actions) : void 0;
1208
+ }
1209
+ async *stream(request) {
1210
+ const client = await this.getClient();
1211
+ const { options } = this.buildRequestOptions(request);
3064
1212
  const messageId = core.generateMessageId();
3065
1213
  yield { type: "message:start", id: messageId };
3066
1214
  try {
3067
- const stream = await client.chat.completions.create({
3068
- // Azure uses deployment name, not model name
3069
- model: this.config.deploymentName,
3070
- messages,
3071
- tools,
3072
- temperature: request.config?.temperature ?? this.config.temperature,
3073
- max_tokens: request.config?.maxTokens ?? this.config.maxTokens,
3074
- stream: true
3075
- });
3076
- let currentToolCall = null;
3077
- for await (const chunk of stream) {
1215
+ const stream = await client.messages.stream(options);
1216
+ let currentToolUse = null;
1217
+ let isInThinkingBlock = false;
1218
+ for await (const event of stream) {
3078
1219
  if (request.signal?.aborted) {
3079
1220
  break;
3080
1221
  }
3081
- const delta = chunk.choices[0]?.delta;
3082
- if (delta?.content) {
3083
- yield { type: "message:delta", content: delta.content };
3084
- }
3085
- if (delta?.tool_calls) {
3086
- for (const toolCall of delta.tool_calls) {
3087
- if (toolCall.id) {
3088
- if (currentToolCall) {
3089
- yield {
3090
- type: "action:args",
3091
- id: currentToolCall.id,
3092
- args: currentToolCall.arguments
3093
- };
3094
- }
3095
- currentToolCall = {
3096
- id: toolCall.id,
3097
- name: toolCall.function?.name || "",
3098
- arguments: toolCall.function?.arguments || ""
1222
+ switch (event.type) {
1223
+ case "content_block_start":
1224
+ if (event.content_block.type === "tool_use") {
1225
+ currentToolUse = {
1226
+ id: event.content_block.id,
1227
+ name: event.content_block.name,
1228
+ input: ""
1229
+ };
1230
+ yield {
1231
+ type: "action:start",
1232
+ id: currentToolUse.id,
1233
+ name: currentToolUse.name
3099
1234
  };
1235
+ } else if (event.content_block.type === "thinking") {
1236
+ isInThinkingBlock = true;
1237
+ yield { type: "thinking:start" };
1238
+ }
1239
+ break;
1240
+ case "content_block_delta":
1241
+ if (event.delta.type === "text_delta") {
1242
+ yield { type: "message:delta", content: event.delta.text };
1243
+ } else if (event.delta.type === "thinking_delta") {
1244
+ yield { type: "thinking:delta", content: event.delta.thinking };
1245
+ } else if (event.delta.type === "input_json_delta" && currentToolUse) {
1246
+ currentToolUse.input += event.delta.partial_json;
1247
+ }
1248
+ break;
1249
+ case "content_block_stop":
1250
+ if (currentToolUse) {
3100
1251
  yield {
3101
- type: "action:start",
3102
- id: currentToolCall.id,
3103
- name: currentToolCall.name
1252
+ type: "action:args",
1253
+ id: currentToolUse.id,
1254
+ args: currentToolUse.input
3104
1255
  };
3105
- } else if (currentToolCall && toolCall.function?.arguments) {
3106
- currentToolCall.arguments += toolCall.function.arguments;
1256
+ currentToolUse = null;
3107
1257
  }
3108
- }
3109
- }
3110
- if (chunk.choices[0]?.finish_reason) {
3111
- if (currentToolCall) {
3112
- yield {
3113
- type: "action:args",
3114
- id: currentToolCall.id,
3115
- args: currentToolCall.arguments
3116
- };
3117
- }
1258
+ if (isInThinkingBlock) {
1259
+ yield { type: "thinking:end" };
1260
+ isInThinkingBlock = false;
1261
+ }
1262
+ break;
1263
+ case "message_stop":
1264
+ break;
3118
1265
  }
3119
1266
  }
3120
1267
  yield { type: "message:end" };
@@ -3123,57 +1270,88 @@ var AzureAdapter = class {
3123
1270
  yield {
3124
1271
  type: "error",
3125
1272
  message: error instanceof Error ? error.message : "Unknown error",
3126
- code: "AZURE_ERROR"
1273
+ code: "ANTHROPIC_ERROR"
3127
1274
  };
3128
1275
  }
3129
1276
  }
3130
- /**
3131
- * Non-streaming completion (optional, for debugging)
3132
- */
3133
- async complete(request) {
3134
- const client = await this.getClient();
3135
- let messages;
3136
- if (request.rawMessages && request.rawMessages.length > 0) {
3137
- messages = request.rawMessages;
3138
- if (request.systemPrompt) {
3139
- const hasSystem = messages.some((m) => m.role === "system");
3140
- if (!hasSystem) {
3141
- messages = [
3142
- { role: "system", content: request.systemPrompt },
3143
- ...messages
3144
- ];
1277
+ };
1278
+ function createAnthropicAdapter(config) {
1279
+ return new AnthropicAdapter(config);
1280
+ }
1281
+ var OllamaAdapter = class {
1282
+ constructor(config = {}) {
1283
+ this.provider = "ollama";
1284
+ this.config = config;
1285
+ this.model = config.model || "llama3";
1286
+ this.baseUrl = config.baseUrl || "http://localhost:11434";
1287
+ }
1288
+ async *stream(request) {
1289
+ const messages = formatMessages(request.messages, request.systemPrompt);
1290
+ const messageId = core.generateMessageId();
1291
+ yield { type: "message:start", id: messageId };
1292
+ try {
1293
+ const response = await fetch(`${this.baseUrl}/api/chat`, {
1294
+ method: "POST",
1295
+ headers: {
1296
+ "Content-Type": "application/json"
1297
+ },
1298
+ body: JSON.stringify({
1299
+ model: request.config?.model || this.model,
1300
+ messages,
1301
+ stream: true,
1302
+ options: {
1303
+ temperature: request.config?.temperature ?? this.config.temperature,
1304
+ num_predict: request.config?.maxTokens ?? this.config.maxTokens
1305
+ }
1306
+ }),
1307
+ signal: request.signal
1308
+ });
1309
+ if (!response.ok) {
1310
+ throw new Error(`Ollama API error: ${response.status}`);
1311
+ }
1312
+ if (!response.body) {
1313
+ throw new Error("No response body");
1314
+ }
1315
+ const reader = response.body.getReader();
1316
+ const decoder = new TextDecoder();
1317
+ let buffer = "";
1318
+ while (true) {
1319
+ const { done, value } = await reader.read();
1320
+ if (done) break;
1321
+ buffer += decoder.decode(value, { stream: true });
1322
+ const lines = buffer.split("\n");
1323
+ buffer = lines.pop() || "";
1324
+ for (const line of lines) {
1325
+ if (!line.trim()) continue;
1326
+ try {
1327
+ const chunk = JSON.parse(line);
1328
+ if (chunk.message?.content) {
1329
+ yield { type: "message:delta", content: chunk.message.content };
1330
+ }
1331
+ if (chunk.done) {
1332
+ break;
1333
+ }
1334
+ } catch {
1335
+ }
3145
1336
  }
3146
1337
  }
3147
- } else {
3148
- messages = formatMessagesForOpenAI2(
3149
- request.messages,
3150
- request.systemPrompt
3151
- );
1338
+ yield { type: "message:end" };
1339
+ yield { type: "done" };
1340
+ } catch (error) {
1341
+ if (error.name === "AbortError") {
1342
+ yield { type: "done" };
1343
+ } else {
1344
+ yield {
1345
+ type: "error",
1346
+ message: error instanceof Error ? error.message : "Unknown error",
1347
+ code: "OLLAMA_ERROR"
1348
+ };
1349
+ }
3152
1350
  }
3153
- const tools = request.actions?.length ? formatTools(request.actions) : void 0;
3154
- const response = await client.chat.completions.create({
3155
- model: this.config.deploymentName,
3156
- messages,
3157
- tools,
3158
- temperature: request.config?.temperature ?? this.config.temperature,
3159
- max_tokens: request.config?.maxTokens ?? this.config.maxTokens
3160
- });
3161
- const choice = response.choices[0];
3162
- const message = choice?.message;
3163
- const toolCalls = (message?.tool_calls || []).map((tc) => ({
3164
- id: tc.id,
3165
- name: tc.function.name,
3166
- args: JSON.parse(tc.function.arguments || "{}")
3167
- }));
3168
- return {
3169
- content: message?.content || "",
3170
- toolCalls,
3171
- rawResponse: response
3172
- };
3173
1351
  }
3174
1352
  };
3175
- function createAzureAdapter(config) {
3176
- return new AzureAdapter(config);
1353
+ function createOllamaAdapter(config) {
1354
+ return new OllamaAdapter(config);
3177
1355
  }
3178
1356
 
3179
1357
  // src/server/streaming.ts
@@ -4411,766 +2589,6 @@ function createNodeHandler(config) {
4411
2589
  return app.fetch;
4412
2590
  }
4413
2591
 
4414
- // src/providers/registry.ts
4415
- var providerFactories = /* @__PURE__ */ new Map();
4416
- function registerProvider(name, factory) {
4417
- providerFactories.set(name, factory);
4418
- }
4419
- function getProvider(name, config) {
4420
- const factory = providerFactories.get(name);
4421
- if (!factory) {
4422
- return void 0;
4423
- }
4424
- return factory(config);
4425
- }
4426
- function hasProvider(name) {
4427
- return providerFactories.has(name);
4428
- }
4429
- function listProviders() {
4430
- return Array.from(providerFactories.keys());
4431
- }
4432
- function getAvailableProviders() {
4433
- const result = [];
4434
- for (const [name, factory] of providerFactories) {
4435
- try {
4436
- const provider = factory();
4437
- result.push({
4438
- name,
4439
- models: provider.supportedModels
4440
- });
4441
- } catch {
4442
- result.push({
4443
- name,
4444
- models: []
4445
- });
4446
- }
4447
- }
4448
- return result;
4449
- }
4450
- function getModelCapabilities(providerName, modelId) {
4451
- const provider = getProvider(providerName);
4452
- if (!provider) {
4453
- return void 0;
4454
- }
4455
- return provider.getCapabilities(modelId);
4456
- }
4457
-
4458
- // src/providers/openai/index.ts
4459
- var OPENAI_MODELS2 = {
4460
- // GPT-4o series
4461
- "gpt-4o": {
4462
- vision: true,
4463
- tools: true,
4464
- audio: true,
4465
- jsonMode: true,
4466
- maxTokens: 128e3
4467
- },
4468
- "gpt-4o-mini": {
4469
- vision: true,
4470
- tools: true,
4471
- audio: false,
4472
- jsonMode: true,
4473
- maxTokens: 128e3
4474
- },
4475
- "gpt-4o-2024-11-20": {
4476
- vision: true,
4477
- tools: true,
4478
- audio: true,
4479
- jsonMode: true,
4480
- maxTokens: 128e3
4481
- },
4482
- "gpt-4o-2024-08-06": {
4483
- vision: true,
4484
- tools: true,
4485
- audio: false,
4486
- jsonMode: true,
4487
- maxTokens: 128e3
4488
- },
4489
- // GPT-4 Turbo series
4490
- "gpt-4-turbo": {
4491
- vision: true,
4492
- tools: true,
4493
- audio: false,
4494
- jsonMode: true,
4495
- maxTokens: 128e3
4496
- },
4497
- "gpt-4-turbo-preview": {
4498
- vision: false,
4499
- tools: true,
4500
- audio: false,
4501
- jsonMode: true,
4502
- maxTokens: 128e3
4503
- },
4504
- // GPT-4 series
4505
- "gpt-4": {
4506
- vision: false,
4507
- tools: true,
4508
- audio: false,
4509
- jsonMode: false,
4510
- maxTokens: 8192
4511
- },
4512
- "gpt-4-32k": {
4513
- vision: false,
4514
- tools: true,
4515
- audio: false,
4516
- jsonMode: false,
4517
- maxTokens: 32768
4518
- },
4519
- // GPT-3.5 series
4520
- "gpt-3.5-turbo": {
4521
- vision: false,
4522
- tools: true,
4523
- audio: false,
4524
- jsonMode: true,
4525
- maxTokens: 16385
4526
- },
4527
- "gpt-3.5-turbo-16k": {
4528
- vision: false,
4529
- tools: true,
4530
- audio: false,
4531
- jsonMode: true,
4532
- maxTokens: 16385
4533
- },
4534
- // O1 reasoning series
4535
- o1: {
4536
- vision: true,
4537
- tools: false,
4538
- // O1 doesn't support tools yet
4539
- audio: false,
4540
- jsonMode: false,
4541
- maxTokens: 128e3
4542
- },
4543
- "o1-mini": {
4544
- vision: true,
4545
- tools: false,
4546
- audio: false,
4547
- jsonMode: false,
4548
- maxTokens: 128e3
4549
- },
4550
- "o1-preview": {
4551
- vision: true,
4552
- tools: false,
4553
- audio: false,
4554
- jsonMode: false,
4555
- maxTokens: 128e3
4556
- },
4557
- // O3 reasoning series
4558
- "o3-mini": {
4559
- vision: true,
4560
- tools: false,
4561
- audio: false,
4562
- jsonMode: false,
4563
- maxTokens: 128e3
4564
- }
4565
- };
4566
- function createOpenAI(config = {}) {
4567
- const apiKey = config.apiKey ?? process.env.OPENAI_API_KEY ?? "";
4568
- return {
4569
- name: "openai",
4570
- supportedModels: Object.keys(OPENAI_MODELS2),
4571
- languageModel(modelId) {
4572
- return createOpenAIAdapter({
4573
- apiKey,
4574
- model: modelId,
4575
- baseUrl: config.baseUrl
4576
- });
4577
- },
4578
- getCapabilities(modelId) {
4579
- const model = OPENAI_MODELS2[modelId] ?? OPENAI_MODELS2["gpt-4o"];
4580
- return {
4581
- supportsVision: model.vision,
4582
- supportsTools: model.tools,
4583
- supportsThinking: false,
4584
- // OpenAI doesn't have extended thinking
4585
- supportsStreaming: true,
4586
- supportsPDF: false,
4587
- // OpenAI doesn't support PDFs directly
4588
- supportsAudio: model.audio,
4589
- supportsVideo: false,
4590
- maxTokens: model.maxTokens,
4591
- supportedImageTypes: model.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : [],
4592
- supportedAudioTypes: model.audio ? ["audio/mp3", "audio/wav", "audio/webm"] : [],
4593
- supportsJsonMode: model.jsonMode,
4594
- supportsSystemMessages: true
4595
- };
4596
- }
4597
- };
4598
- }
4599
-
4600
- // src/providers/anthropic/index.ts
4601
- var ANTHROPIC_MODELS2 = {
4602
- // Claude 4 series (latest)
4603
- "claude-sonnet-4-20250514": {
4604
- vision: true,
4605
- tools: true,
4606
- thinking: true,
4607
- maxTokens: 64e3
4608
- },
4609
- "claude-opus-4-20250514": {
4610
- vision: true,
4611
- tools: true,
4612
- thinking: true,
4613
- maxTokens: 32e3
4614
- },
4615
- // Claude 3.5 series
4616
- "claude-3-5-sonnet-latest": {
4617
- vision: true,
4618
- tools: true,
4619
- thinking: true,
4620
- maxTokens: 2e5
4621
- },
4622
- "claude-3-5-sonnet-20241022": {
4623
- vision: true,
4624
- tools: true,
4625
- thinking: true,
4626
- maxTokens: 2e5
4627
- },
4628
- "claude-3-5-haiku-latest": {
4629
- vision: true,
4630
- tools: true,
4631
- thinking: false,
4632
- maxTokens: 2e5
4633
- },
4634
- "claude-3-5-haiku-20241022": {
4635
- vision: true,
4636
- tools: true,
4637
- thinking: false,
4638
- maxTokens: 2e5
4639
- },
4640
- // Claude 3 series
4641
- "claude-3-opus-latest": {
4642
- vision: true,
4643
- tools: true,
4644
- thinking: true,
4645
- maxTokens: 2e5
4646
- },
4647
- "claude-3-opus-20240229": {
4648
- vision: true,
4649
- tools: true,
4650
- thinking: true,
4651
- maxTokens: 2e5
4652
- },
4653
- "claude-3-sonnet-20240229": {
4654
- vision: true,
4655
- tools: true,
4656
- thinking: false,
4657
- maxTokens: 2e5
4658
- },
4659
- "claude-3-haiku-20240307": {
4660
- vision: true,
4661
- tools: true,
4662
- thinking: false,
4663
- maxTokens: 2e5
4664
- }
4665
- };
4666
- function createAnthropic(config = {}) {
4667
- const apiKey = config.apiKey ?? process.env.ANTHROPIC_API_KEY ?? "";
4668
- return {
4669
- name: "anthropic",
4670
- supportedModels: Object.keys(ANTHROPIC_MODELS2),
4671
- languageModel(modelId) {
4672
- return createAnthropicAdapter({
4673
- apiKey,
4674
- model: modelId,
4675
- baseUrl: config.baseUrl,
4676
- thinking: config.thinkingBudget ? { type: "enabled", budgetTokens: config.thinkingBudget } : void 0
4677
- });
4678
- },
4679
- getCapabilities(modelId) {
4680
- const model = ANTHROPIC_MODELS2[modelId] ?? ANTHROPIC_MODELS2["claude-3-5-sonnet-latest"];
4681
- return {
4682
- supportsVision: model.vision,
4683
- supportsTools: model.tools,
4684
- supportsThinking: model.thinking,
4685
- supportsStreaming: true,
4686
- supportsPDF: true,
4687
- // Claude supports PDFs
4688
- supportsAudio: false,
4689
- supportsVideo: false,
4690
- maxTokens: model.maxTokens,
4691
- supportedImageTypes: [
4692
- "image/png",
4693
- "image/jpeg",
4694
- "image/gif",
4695
- "image/webp"
4696
- ],
4697
- supportsJsonMode: false,
4698
- // Anthropic doesn't have JSON mode
4699
- supportsSystemMessages: true
4700
- };
4701
- }
4702
- };
4703
- }
4704
-
4705
- // src/providers/ollama/index.ts
4706
- var OLLAMA_MODELS = {
4707
- // Llama series
4708
- llama3: {
4709
- vision: false,
4710
- tools: true,
4711
- maxTokens: 8192
4712
- },
4713
- "llama3:70b": {
4714
- vision: false,
4715
- tools: true,
4716
- maxTokens: 8192
4717
- },
4718
- "llama3.2": {
4719
- vision: false,
4720
- tools: true,
4721
- maxTokens: 8192
4722
- },
4723
- "llama3.2-vision": {
4724
- vision: true,
4725
- tools: true,
4726
- maxTokens: 8192
4727
- },
4728
- // Mistral series
4729
- mistral: {
4730
- vision: false,
4731
- tools: true,
4732
- maxTokens: 8192
4733
- },
4734
- "mistral-nemo": {
4735
- vision: false,
4736
- tools: true,
4737
- maxTokens: 128e3
4738
- },
4739
- mixtral: {
4740
- vision: false,
4741
- tools: true,
4742
- maxTokens: 32768
4743
- },
4744
- // CodeLlama
4745
- codellama: {
4746
- vision: false,
4747
- tools: false,
4748
- maxTokens: 16384
4749
- },
4750
- // Phi series
4751
- phi3: {
4752
- vision: false,
4753
- tools: true,
4754
- maxTokens: 4096
4755
- },
4756
- "phi3:medium": {
4757
- vision: false,
4758
- tools: true,
4759
- maxTokens: 4096
4760
- },
4761
- // Gemma series
4762
- gemma2: {
4763
- vision: false,
4764
- tools: false,
4765
- maxTokens: 8192
4766
- },
4767
- "gemma2:27b": {
4768
- vision: false,
4769
- tools: false,
4770
- maxTokens: 8192
4771
- },
4772
- // Qwen series
4773
- qwen2: {
4774
- vision: false,
4775
- tools: true,
4776
- maxTokens: 32768
4777
- },
4778
- "qwen2.5-coder": {
4779
- vision: false,
4780
- tools: true,
4781
- maxTokens: 32768
4782
- },
4783
- // LLaVA (vision)
4784
- llava: {
4785
- vision: true,
4786
- tools: false,
4787
- maxTokens: 4096
4788
- },
4789
- // DeepSeek
4790
- deepseek: {
4791
- vision: false,
4792
- tools: true,
4793
- maxTokens: 16384
4794
- },
4795
- "deepseek-coder": {
4796
- vision: false,
4797
- tools: false,
4798
- maxTokens: 16384
4799
- }
4800
- };
4801
- var DEFAULT_MODEL_CAPS = {
4802
- vision: false,
4803
- tools: false,
4804
- maxTokens: 4096
4805
- };
4806
- function createOllama(config = {}) {
4807
- const baseUrl = config.baseUrl ?? "http://localhost:11434";
4808
- return {
4809
- name: "ollama",
4810
- supportedModels: Object.keys(OLLAMA_MODELS),
4811
- languageModel(modelId) {
4812
- return createOllamaAdapter({
4813
- model: modelId,
4814
- baseUrl
4815
- });
4816
- },
4817
- getCapabilities(modelId) {
4818
- const baseModelName = modelId.split(":")[0];
4819
- const model = OLLAMA_MODELS[modelId] ?? OLLAMA_MODELS[baseModelName] ?? DEFAULT_MODEL_CAPS;
4820
- return {
4821
- supportsVision: model.vision,
4822
- supportsTools: model.tools,
4823
- supportsThinking: false,
4824
- supportsStreaming: true,
4825
- supportsPDF: false,
4826
- supportsAudio: false,
4827
- supportsVideo: false,
4828
- maxTokens: model.maxTokens,
4829
- supportedImageTypes: model.vision ? ["image/png", "image/jpeg", "image/gif"] : [],
4830
- supportsJsonMode: false,
4831
- supportsSystemMessages: true
4832
- };
4833
- }
4834
- };
4835
- }
4836
-
4837
- // src/providers/google/index.ts
4838
- var GOOGLE_MODELS2 = {
4839
- // Gemini 2.0 series (latest)
4840
- "gemini-2.0-flash": {
4841
- vision: true,
4842
- tools: true,
4843
- audio: true,
4844
- video: true,
4845
- pdf: true,
4846
- maxTokens: 1e6,
4847
- outputTokens: 8192
4848
- },
4849
- "gemini-2.0-flash-lite": {
4850
- vision: true,
4851
- tools: true,
4852
- audio: false,
4853
- video: false,
4854
- pdf: true,
4855
- maxTokens: 1e6,
4856
- outputTokens: 8192
4857
- },
4858
- // Gemini 2.5 series (experimental)
4859
- "gemini-2.5-pro-preview-05-06": {
4860
- vision: true,
4861
- tools: true,
4862
- audio: true,
4863
- video: true,
4864
- pdf: true,
4865
- maxTokens: 1e6,
4866
- outputTokens: 65536
4867
- },
4868
- "gemini-2.5-flash-preview-05-20": {
4869
- vision: true,
4870
- tools: true,
4871
- audio: true,
4872
- video: true,
4873
- pdf: true,
4874
- maxTokens: 1e6,
4875
- outputTokens: 65536
4876
- },
4877
- // Gemini 1.5 series
4878
- "gemini-1.5-pro": {
4879
- vision: true,
4880
- tools: true,
4881
- audio: true,
4882
- video: true,
4883
- pdf: true,
4884
- maxTokens: 2e6,
4885
- outputTokens: 8192
4886
- },
4887
- "gemini-1.5-pro-latest": {
4888
- vision: true,
4889
- tools: true,
4890
- audio: true,
4891
- video: true,
4892
- pdf: true,
4893
- maxTokens: 2e6,
4894
- outputTokens: 8192
4895
- },
4896
- "gemini-1.5-flash": {
4897
- vision: true,
4898
- tools: true,
4899
- audio: true,
4900
- video: true,
4901
- pdf: true,
4902
- maxTokens: 1e6,
4903
- outputTokens: 8192
4904
- },
4905
- "gemini-1.5-flash-latest": {
4906
- vision: true,
4907
- tools: true,
4908
- audio: true,
4909
- video: true,
4910
- pdf: true,
4911
- maxTokens: 1e6,
4912
- outputTokens: 8192
4913
- },
4914
- "gemini-1.5-flash-8b": {
4915
- vision: true,
4916
- tools: true,
4917
- audio: false,
4918
- video: false,
4919
- pdf: true,
4920
- maxTokens: 1e6,
4921
- outputTokens: 8192
4922
- },
4923
- // Gemini 1.0 series (legacy)
4924
- "gemini-1.0-pro": {
4925
- vision: false,
4926
- tools: true,
4927
- audio: false,
4928
- video: false,
4929
- pdf: false,
4930
- maxTokens: 30720,
4931
- outputTokens: 2048
4932
- }
4933
- };
4934
- function createGoogle(config = {}) {
4935
- const apiKey = config.apiKey ?? process.env.GOOGLE_API_KEY ?? "";
4936
- return {
4937
- name: "google",
4938
- supportedModels: Object.keys(GOOGLE_MODELS2),
4939
- languageModel(modelId) {
4940
- return createGoogleAdapter({
4941
- apiKey,
4942
- model: modelId,
4943
- baseUrl: config.baseUrl,
4944
- safetySettings: config.safetySettings
4945
- });
4946
- },
4947
- getCapabilities(modelId) {
4948
- const model = GOOGLE_MODELS2[modelId] ?? GOOGLE_MODELS2["gemini-2.0-flash"];
4949
- return {
4950
- supportsVision: model.vision,
4951
- supportsTools: model.tools,
4952
- supportsThinking: false,
4953
- // Gemini doesn't have extended thinking like Claude
4954
- supportsStreaming: true,
4955
- supportsPDF: model.pdf,
4956
- supportsAudio: model.audio,
4957
- supportsVideo: model.video,
4958
- maxTokens: model.maxTokens,
4959
- supportedImageTypes: model.vision ? [
4960
- "image/png",
4961
- "image/jpeg",
4962
- "image/gif",
4963
- "image/webp",
4964
- "image/heic",
4965
- "image/heif"
4966
- ] : [],
4967
- supportedAudioTypes: model.audio ? [
4968
- "audio/mp3",
4969
- "audio/wav",
4970
- "audio/aiff",
4971
- "audio/aac",
4972
- "audio/ogg",
4973
- "audio/flac"
4974
- ] : [],
4975
- supportedVideoTypes: model.video ? [
4976
- "video/mp4",
4977
- "video/mpeg",
4978
- "video/mov",
4979
- "video/avi",
4980
- "video/webm",
4981
- "video/mkv"
4982
- ] : [],
4983
- supportsJsonMode: true,
4984
- // Gemini supports JSON mode
4985
- supportsSystemMessages: true
4986
- };
4987
- }
4988
- };
4989
- }
4990
-
4991
- // src/providers/xai/index.ts
4992
- var XAI_MODELS2 = {
4993
- // Grok 4.1 Fast (Latest - December 2025)
4994
- "grok-4-1-fast-reasoning": {
4995
- vision: false,
4996
- tools: true,
4997
- maxTokens: 2e6,
4998
- outputTokens: 16384
4999
- },
5000
- "grok-4-1-fast-non-reasoning": {
5001
- vision: false,
5002
- tools: true,
5003
- maxTokens: 2e6,
5004
- outputTokens: 16384
5005
- },
5006
- // Grok 4 Fast (September 2025)
5007
- "grok-4-fast-reasoning": {
5008
- vision: false,
5009
- tools: true,
5010
- maxTokens: 2e6,
5011
- outputTokens: 16384
5012
- },
5013
- "grok-4-fast-non-reasoning": {
5014
- vision: false,
5015
- tools: true,
5016
- maxTokens: 2e6,
5017
- outputTokens: 16384
5018
- },
5019
- // Grok 4 (July 2025)
5020
- "grok-4": {
5021
- vision: true,
5022
- tools: true,
5023
- maxTokens: 256e3,
5024
- outputTokens: 16384
5025
- },
5026
- "grok-4-0709": {
5027
- vision: true,
5028
- tools: true,
5029
- maxTokens: 256e3,
5030
- outputTokens: 16384
5031
- },
5032
- // Grok 3 (February 2025) - Stable
5033
- "grok-3-beta": {
5034
- vision: true,
5035
- tools: true,
5036
- maxTokens: 131072,
5037
- outputTokens: 8192
5038
- },
5039
- "grok-3-fast-beta": {
5040
- vision: false,
5041
- tools: true,
5042
- maxTokens: 131072,
5043
- outputTokens: 8192
5044
- },
5045
- "grok-3-mini-beta": {
5046
- vision: false,
5047
- tools: true,
5048
- maxTokens: 32768,
5049
- outputTokens: 8192
5050
- },
5051
- "grok-3-mini-fast-beta": {
5052
- vision: false,
5053
- tools: true,
5054
- maxTokens: 32768,
5055
- outputTokens: 8192
5056
- },
5057
- // Grok Code Fast (August 2025)
5058
- "grok-code-fast-1": {
5059
- vision: false,
5060
- tools: true,
5061
- maxTokens: 256e3,
5062
- outputTokens: 16384
5063
- },
5064
- // Grok 2 (Legacy - for backward compatibility)
5065
- "grok-2": {
5066
- vision: true,
5067
- tools: true,
5068
- maxTokens: 131072,
5069
- outputTokens: 4096
5070
- },
5071
- "grok-2-latest": {
5072
- vision: true,
5073
- tools: true,
5074
- maxTokens: 131072,
5075
- outputTokens: 4096
5076
- },
5077
- "grok-2-mini": {
5078
- vision: false,
5079
- tools: true,
5080
- maxTokens: 131072,
5081
- outputTokens: 4096
5082
- }
5083
- };
5084
- function createXAI(config = {}) {
5085
- const apiKey = config.apiKey ?? process.env.XAI_API_KEY ?? "";
5086
- return {
5087
- name: "xai",
5088
- supportedModels: Object.keys(XAI_MODELS2),
5089
- languageModel(modelId) {
5090
- return createXAIAdapter({
5091
- apiKey,
5092
- model: modelId,
5093
- baseUrl: config.baseUrl
5094
- });
5095
- },
5096
- getCapabilities(modelId) {
5097
- const model = XAI_MODELS2[modelId] ?? XAI_MODELS2["grok-3-fast-beta"];
5098
- return {
5099
- supportsVision: model.vision,
5100
- supportsTools: model.tools,
5101
- supportsThinking: false,
5102
- supportsStreaming: true,
5103
- supportsPDF: false,
5104
- supportsAudio: false,
5105
- supportsVideo: false,
5106
- maxTokens: model.maxTokens,
5107
- supportedImageTypes: model.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : [],
5108
- supportsJsonMode: false,
5109
- // xAI doesn't support JSON mode yet
5110
- supportsSystemMessages: true
5111
- };
5112
- }
5113
- };
5114
- }
5115
-
5116
- // src/providers/azure/index.ts
5117
- function detectCapabilitiesFromDeployment(deploymentName) {
5118
- const name = deploymentName.toLowerCase();
5119
- if (name.includes("gpt-4o") || name.includes("gpt4o")) {
5120
- return { vision: true, tools: true, maxTokens: 128e3 };
5121
- }
5122
- if ((name.includes("gpt-4") || name.includes("gpt4")) && (name.includes("turbo") || name.includes("vision"))) {
5123
- return { vision: true, tools: true, maxTokens: 128e3 };
5124
- }
5125
- if (name.includes("gpt-4") || name.includes("gpt4")) {
5126
- return { vision: false, tools: true, maxTokens: 8192 };
5127
- }
5128
- if (name.includes("gpt-35") || name.includes("gpt-3.5") || name.includes("gpt35")) {
5129
- return { vision: false, tools: true, maxTokens: 16385 };
5130
- }
5131
- if (name.includes("o1")) {
5132
- return { vision: true, tools: false, maxTokens: 128e3 };
5133
- }
5134
- return { vision: false, tools: true, maxTokens: 8192 };
5135
- }
5136
- function createAzure(config) {
5137
- const apiKey = config.apiKey ?? process.env.AZURE_OPENAI_API_KEY ?? "";
5138
- const resourceName = config.resourceName ?? process.env.AZURE_OPENAI_RESOURCE ?? "";
5139
- const defaultDeployment = config.deploymentName ?? process.env.AZURE_OPENAI_DEPLOYMENT ?? "";
5140
- const supportedModels = defaultDeployment ? [defaultDeployment] : [];
5141
- return {
5142
- name: "azure",
5143
- supportedModels,
5144
- languageModel(deploymentName) {
5145
- return createAzureAdapter({
5146
- apiKey,
5147
- resourceName,
5148
- deploymentName: deploymentName || defaultDeployment,
5149
- apiVersion: config.apiVersion,
5150
- baseUrl: config.baseUrl
5151
- });
5152
- },
5153
- getCapabilities(deploymentName) {
5154
- const detected = detectCapabilitiesFromDeployment(
5155
- deploymentName || defaultDeployment
5156
- );
5157
- return {
5158
- supportsVision: detected.vision,
5159
- supportsTools: detected.tools,
5160
- supportsThinking: false,
5161
- supportsStreaming: true,
5162
- supportsPDF: false,
5163
- supportsAudio: false,
5164
- supportsVideo: false,
5165
- maxTokens: detected.maxTokens,
5166
- supportedImageTypes: detected.vision ? ["image/png", "image/jpeg", "image/gif", "image/webp"] : [],
5167
- supportsJsonMode: true,
5168
- supportsSystemMessages: true
5169
- };
5170
- }
5171
- };
5172
- }
5173
-
5174
2592
  // src/providers/openai.ts
5175
2593
  function transformTools(tools) {
5176
2594
  return tools.map((tool2) => ({
@@ -5487,20 +2905,6 @@ function getFormatter(provider) {
5487
2905
  }
5488
2906
  return formatter;
5489
2907
  }
5490
- function isProviderSupported(provider) {
5491
- return provider.toLowerCase() in formatters;
5492
- }
5493
- function getSupportedProviders() {
5494
- return Object.keys(formatters);
5495
- }
5496
-
5497
- // src/providers/index.ts
5498
- registerProvider("openai", (config) => createOpenAI(config));
5499
- registerProvider("anthropic", (config) => createAnthropic(config));
5500
- registerProvider("ollama", (config) => createOllama(config));
5501
- registerProvider("google", (config) => createGoogle(config));
5502
- registerProvider("xai", (config) => createXAI(config));
5503
- registerProvider("azure", (config) => createAzure(config));
5504
2908
 
5505
2909
  // src/server/agent-loop.ts
5506
2910
  var DEFAULT_MAX_ITERATIONS = 20;
@@ -5742,58 +3146,24 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
5742
3146
  return results;
5743
3147
  }
5744
3148
 
5745
- exports.AnthropicAdapter = AnthropicAdapter;
5746
- exports.AzureAdapter = AzureAdapter;
5747
3149
  exports.DEFAULT_CAPABILITIES = DEFAULT_CAPABILITIES;
5748
3150
  exports.DEFAULT_MAX_ITERATIONS = DEFAULT_MAX_ITERATIONS;
5749
- exports.GoogleAdapter = GoogleAdapter;
5750
- exports.OllamaAdapter = OllamaAdapter;
5751
- exports.OpenAIAdapter = OpenAIAdapter;
5752
3151
  exports.Runtime = Runtime;
5753
- exports.XAIAdapter = XAIAdapter;
5754
- exports.anthropic = anthropic;
5755
- exports.anthropicFormatter = anthropicFormatter;
5756
- exports.createAnthropic = createAnthropic;
5757
- exports.createAnthropicAdapter = createAnthropicAdapter;
5758
- exports.createAzure = createAzure;
5759
- exports.createAzureAdapter = createAzureAdapter;
5760
3152
  exports.createEventStream = createEventStream;
5761
3153
  exports.createExpressMiddleware = createExpressMiddleware;
5762
- exports.createGoogle = createGoogle;
5763
- exports.createGoogleAdapter = createGoogleAdapter;
5764
3154
  exports.createHonoApp = createHonoApp;
5765
3155
  exports.createNextHandler = createNextHandler;
5766
3156
  exports.createNodeHandler = createNodeHandler;
5767
- exports.createOllama = createOllama;
5768
- exports.createOllamaAdapter = createOllamaAdapter;
5769
- exports.createOpenAI = createOpenAI;
5770
- exports.createOpenAIAdapter = createOpenAIAdapter;
5771
3157
  exports.createRuntime = createRuntime;
5772
3158
  exports.createSSEHeaders = createSSEHeaders;
5773
3159
  exports.createSSEResponse = createSSEResponse;
5774
- exports.createXAI = createXAI;
5775
- exports.createXAIAdapter = createXAIAdapter;
5776
3160
  exports.formatSSEData = formatSSEData;
5777
3161
  exports.formatToolsForAnthropic = formatToolsForAnthropic;
5778
3162
  exports.formatToolsForGoogle = formatToolsForGoogle;
5779
3163
  exports.formatToolsForOpenAI = formatToolsForOpenAI;
5780
- exports.geminiFormatter = geminiFormatter;
5781
3164
  exports.generateText = generateText;
5782
- exports.getAvailableProviders = getAvailableProviders;
5783
- exports.getFormatter = getFormatter;
5784
- exports.getModelCapabilities = getModelCapabilities;
5785
- exports.getProvider = getProvider;
5786
- exports.getSupportedProviders = getSupportedProviders;
5787
- exports.google = google;
5788
- exports.hasProvider = hasProvider;
5789
- exports.isProviderSupported = isProviderSupported;
5790
- exports.listProviders = listProviders;
5791
- exports.openai = openai;
5792
- exports.openaiFormatter = openaiFormatter;
5793
- exports.registerProvider = registerProvider;
5794
3165
  exports.runAgentLoop = runAgentLoop;
5795
3166
  exports.streamText = streamText;
5796
3167
  exports.tool = tool;
5797
- exports.xai = xai;
5798
3168
  //# sourceMappingURL=index.js.map
5799
3169
  //# sourceMappingURL=index.js.map