@yourgpt/llm-sdk 1.2.5 → 1.3.0

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