@absolutejs/absolute 0.19.0-beta.247 → 0.19.0-beta.249

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.
Files changed (42) hide show
  1. package/.absolutejs/eslint-cache +1 -1
  2. package/.absolutejs/prettier.cache.json +17 -13
  3. package/.absolutejs/vue-tsc.tsbuildinfo +1 -1
  4. package/.claude/settings.local.json +2 -1
  5. package/.playwright-mcp/page-2026-03-31T16-05-33-392Z.png +0 -0
  6. package/dist/ai/index.js +667 -11
  7. package/dist/ai/index.js.map +10 -8
  8. package/dist/ai/providers/anthropic.js +7 -1
  9. package/dist/ai/providers/anthropic.js.map +3 -3
  10. package/dist/ai/providers/gemini.js +331 -0
  11. package/dist/ai/providers/gemini.js.map +10 -0
  12. package/dist/ai/providers/ollama.js.map +2 -2
  13. package/dist/ai/providers/openai.js +11 -3
  14. package/dist/ai/providers/openai.js.map +3 -3
  15. package/dist/ai/providers/openaiCompatible.js +11 -3
  16. package/dist/ai/providers/openaiCompatible.js.map +4 -4
  17. package/dist/ai/providers/openaiResponses.js +432 -0
  18. package/dist/ai/providers/openaiResponses.js.map +10 -0
  19. package/dist/ai-client/angular/ai/index.js +61 -1
  20. package/dist/ai-client/react/ai/index.js +61 -1
  21. package/dist/ai-client/vue/ai/index.js +61 -1
  22. package/dist/angular/ai/index.js +62 -2
  23. package/dist/angular/ai/index.js.map +5 -5
  24. package/dist/build.js +3 -1
  25. package/dist/build.js.map +3 -3
  26. package/dist/index.js +3 -1
  27. package/dist/index.js.map +3 -3
  28. package/dist/react/ai/index.js +62 -2
  29. package/dist/react/ai/index.js.map +6 -6
  30. package/dist/src/ai/client/actions.d.ts +43 -0
  31. package/dist/src/ai/index.d.ts +2 -0
  32. package/dist/src/ai/providers/gemini.d.ts +7 -0
  33. package/dist/src/ai/providers/openaiResponses.d.ts +10 -0
  34. package/dist/svelte/ai/index.js +62 -2
  35. package/dist/svelte/ai/index.js.map +5 -5
  36. package/dist/types/ai.d.ts +48 -3
  37. package/dist/vue/ai/index.js +62 -2
  38. package/dist/vue/ai/index.js.map +5 -5
  39. package/package.json +9 -1
  40. package/scripts/build.ts +2 -0
  41. package/types/ai.ts +80 -6
  42. package/types/typeGuards.ts +11 -0
package/dist/ai/index.js CHANGED
@@ -106,6 +106,8 @@ var isValidAIClientMessage = (data) => {
106
106
  return "content" in data && typeof data.content === "string" && "messageId" in data && "conversationId" in data;
107
107
  case "tool_status":
108
108
  return "name" in data && "status" in data && "messageId" in data && "conversationId" in data;
109
+ case "image":
110
+ return "data" in data && typeof data.data === "string" && "format" in data && typeof data.format === "string" && "isPartial" in data && typeof data.isPartial === "boolean" && "messageId" in data && "conversationId" in data;
109
111
  case "complete":
110
112
  return "messageId" in data && "conversationId" in data;
111
113
  case "error":
@@ -207,8 +209,8 @@ var mapOpenAIContent = (msg) => {
207
209
  if (typeof msg.content === "string") {
208
210
  return msg.content;
209
211
  }
210
- const hasImages = msg.content.some((block) => block.type === "image");
211
- if (!hasImages) {
212
+ const hasMedia = msg.content.some((block) => block.type === "image" || block.type === "document");
213
+ if (!hasMedia) {
212
214
  return null;
213
215
  }
214
216
  const blocks = [];
@@ -220,6 +222,14 @@ var mapOpenAIContent = (msg) => {
220
222
  },
221
223
  type: "image_url"
222
224
  });
225
+ } else if (block.type === "document") {
226
+ blocks.push({
227
+ file: {
228
+ file_data: `data:${block.source.media_type};base64,${block.source.data}`,
229
+ filename: block.name ?? "document.pdf"
230
+ },
231
+ type: "file"
232
+ });
223
233
  } else if (block.type === "text") {
224
234
  blocks.push({ text: block.content, type: "text" });
225
235
  }
@@ -473,6 +483,607 @@ var moonshot = (config) => openaiCompatible({
473
483
  baseUrl: "https://api.moonshot.ai"
474
484
  });
475
485
 
486
+ // src/ai/providers/openaiResponses.ts
487
+ var DEFAULT_BASE_URL2 = "https://api.openai.com";
488
+ var EVENT_PREFIX_LENGTH = 7;
489
+ var DATA_PREFIX_LENGTH = 6;
490
+ var isRecord2 = (value) => typeof value === "object" && value !== null;
491
+ var isRecordArray2 = (value) => Array.isArray(value) && value.length > 0 && isRecord2(value[0]);
492
+ var mapContentToResponsesFormat = (content) => {
493
+ if (typeof content === "string") {
494
+ return content;
495
+ }
496
+ const parts = [];
497
+ for (const block of content) {
498
+ if (block.type === "text") {
499
+ parts.push({ text: block.content, type: "input_text" });
500
+ } else if (block.type === "image") {
501
+ parts.push({
502
+ image_url: {
503
+ url: `data:${block.source.media_type};base64,${block.source.data}`
504
+ },
505
+ type: "input_image"
506
+ });
507
+ } else if (block.type === "document") {
508
+ parts.push({
509
+ file: {
510
+ file_data: `data:${block.source.media_type};base64,${block.source.data}`,
511
+ filename: block.name ?? "document.pdf"
512
+ },
513
+ type: "input_file"
514
+ });
515
+ }
516
+ }
517
+ return parts.length > 0 ? parts : "";
518
+ };
519
+ var hasToolBlocks = (content) => content.some((block) => block.type === "tool_use" || block.type === "tool_result");
520
+ var convertToolBlocks = (content) => {
521
+ const items = [];
522
+ for (const block of content) {
523
+ if (block.type === "tool_use") {
524
+ items.push({
525
+ arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input),
526
+ call_id: block.id,
527
+ name: block.name,
528
+ type: "function_call"
529
+ });
530
+ } else if (block.type === "tool_result") {
531
+ items.push({
532
+ call_id: block.tool_use_id,
533
+ output: typeof block.content === "string" ? block.content : "",
534
+ type: "function_call_output"
535
+ });
536
+ }
537
+ }
538
+ return items;
539
+ };
540
+ var convertMessage = (msg) => {
541
+ if (typeof msg.content !== "string" && Array.isArray(msg.content)) {
542
+ if (hasToolBlocks(msg.content)) {
543
+ return convertToolBlocks(msg.content);
544
+ }
545
+ }
546
+ const content = mapContentToResponsesFormat(msg.content);
547
+ return [
548
+ {
549
+ content,
550
+ role: msg.role === "system" ? "developer" : msg.role,
551
+ type: "message"
552
+ }
553
+ ];
554
+ };
555
+ var buildInput = (messages) => {
556
+ const input = [];
557
+ for (const msg of messages) {
558
+ input.push(...convertMessage(msg));
559
+ }
560
+ return input;
561
+ };
562
+ var mapToolDefinition = (tool) => ({
563
+ description: tool.description,
564
+ name: tool.name,
565
+ parameters: tool.input_schema,
566
+ type: "function"
567
+ });
568
+ var buildTools = (tools, imageGeneration) => {
569
+ const result = [];
570
+ if (tools) {
571
+ for (const tool of tools) {
572
+ result.push(mapToolDefinition(tool));
573
+ }
574
+ }
575
+ if (imageGeneration) {
576
+ const imageGenTool = {
577
+ type: "image_generation"
578
+ };
579
+ if (imageGeneration.partialImages !== undefined) {
580
+ imageGenTool.partial_images = imageGeneration.partialImages;
581
+ }
582
+ result.push(imageGenTool);
583
+ }
584
+ return result.length > 0 ? result : undefined;
585
+ };
586
+ var buildRequestBody2 = (params, imageGeneration) => {
587
+ const body = {
588
+ input: buildInput(params.messages),
589
+ model: params.model,
590
+ stream: true
591
+ };
592
+ if (params.systemPrompt) {
593
+ body.instructions = params.systemPrompt;
594
+ }
595
+ const tools = buildTools(params.tools, imageGeneration);
596
+ if (tools) {
597
+ body.tools = tools;
598
+ }
599
+ return body;
600
+ };
601
+ var parseJSON = (data) => {
602
+ try {
603
+ return JSON.parse(data);
604
+ } catch {
605
+ return null;
606
+ }
607
+ };
608
+ var parseToolInput2 = (rawArguments) => {
609
+ try {
610
+ return JSON.parse(rawArguments);
611
+ } catch {
612
+ return rawArguments;
613
+ }
614
+ };
615
+ var extractUsage2 = (response) => {
616
+ if (!isRecord2(response.usage)) {
617
+ return;
618
+ }
619
+ const { usage } = response;
620
+ return {
621
+ inputTokens: typeof usage.input_tokens === "number" ? usage.input_tokens : 0,
622
+ outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0
623
+ };
624
+ };
625
+ var extractMimeFormat = (mimeType) => {
626
+ if (typeof mimeType !== "string") {
627
+ return "png";
628
+ }
629
+ if (mimeType.includes("jpeg"))
630
+ return "jpeg";
631
+ if (mimeType.includes("webp"))
632
+ return "webp";
633
+ return "png";
634
+ };
635
+ var processTextDelta = function* (parsed) {
636
+ if (typeof parsed.delta === "string") {
637
+ yield { content: parsed.delta, type: "text" };
638
+ }
639
+ };
640
+ var processPartialImage = function* (parsed) {
641
+ const itemId = typeof parsed.item_id === "string" ? parsed.item_id : undefined;
642
+ const b64 = typeof parsed.partial_image_b64 === "string" ? parsed.partial_image_b64 : undefined;
643
+ if (b64) {
644
+ yield {
645
+ data: b64,
646
+ format: "png",
647
+ imageId: itemId,
648
+ isPartial: true,
649
+ type: "image"
650
+ };
651
+ }
652
+ };
653
+ var processFunctionCallArgumentsDelta = (parsed, pendingCalls) => {
654
+ const itemId = typeof parsed.item_id === "string" ? parsed.item_id : "";
655
+ const callId = typeof parsed.call_id === "string" ? parsed.call_id : "";
656
+ const delta = typeof parsed.arguments_delta === "string" ? parsed.arguments_delta : "";
657
+ const existing = pendingCalls.get(itemId);
658
+ if (existing) {
659
+ existing.arguments += delta;
660
+ } else {
661
+ pendingCalls.set(itemId, {
662
+ arguments: delta,
663
+ callId,
664
+ name: ""
665
+ });
666
+ }
667
+ };
668
+ var processFunctionCallArgumentsDone = function* (parsed, pendingCalls) {
669
+ const itemId = typeof parsed.item_id === "string" ? parsed.item_id : "";
670
+ const callId = typeof parsed.call_id === "string" ? parsed.call_id : "";
671
+ const fullArgs = typeof parsed.arguments === "string" ? parsed.arguments : "";
672
+ const pending = pendingCalls.get(itemId);
673
+ const name = pending?.name ?? "";
674
+ const args = fullArgs || pending?.arguments || "";
675
+ pendingCalls.delete(itemId);
676
+ yield {
677
+ id: callId,
678
+ input: parseToolInput2(args),
679
+ name,
680
+ type: "tool_use"
681
+ };
682
+ };
683
+ var processOutputItemAdded = (parsed, pendingCalls) => {
684
+ if (!isRecord2(parsed.item)) {
685
+ return;
686
+ }
687
+ const { item } = parsed;
688
+ const itemId = typeof item.id === "string" ? item.id : "";
689
+ const itemType = typeof item.type === "string" ? item.type : "";
690
+ if (itemType === "function_call") {
691
+ const callId = typeof item.call_id === "string" ? item.call_id : "";
692
+ const name = typeof item.name === "string" ? item.name : "";
693
+ pendingCalls.set(itemId, {
694
+ arguments: "",
695
+ callId,
696
+ name
697
+ });
698
+ }
699
+ };
700
+ var extractImageFromOutput = function* (output) {
701
+ for (const item of output) {
702
+ if (item.type !== "image_generation_call") {
703
+ continue;
704
+ }
705
+ if (item.status !== "completed") {
706
+ continue;
707
+ }
708
+ const data = typeof item.result === "string" ? item.result : "";
709
+ if (!data) {
710
+ continue;
711
+ }
712
+ const format = extractMimeFormat(item.output_format);
713
+ const revisedPrompt = typeof item.revised_prompt === "string" ? item.revised_prompt : undefined;
714
+ const imageId = typeof item.id === "string" ? item.id : undefined;
715
+ yield {
716
+ data,
717
+ format,
718
+ imageId,
719
+ isPartial: false,
720
+ revisedPrompt,
721
+ type: "image"
722
+ };
723
+ }
724
+ };
725
+ var processCompleted = function* (parsed) {
726
+ if (!isRecord2(parsed.response)) {
727
+ yield { type: "done", usage: undefined };
728
+ return;
729
+ }
730
+ const { response } = parsed;
731
+ const usage = extractUsage2(response);
732
+ if (isRecordArray2(response.output)) {
733
+ yield* extractImageFromOutput(response.output);
734
+ }
735
+ yield { type: "done", usage };
736
+ };
737
+ var processSSEEvent = function* (eventType, parsed, pendingCalls) {
738
+ switch (eventType) {
739
+ case "response.output_text.delta":
740
+ yield* processTextDelta(parsed);
741
+ break;
742
+ case "response.image_generation_call.partial_image":
743
+ yield* processPartialImage(parsed);
744
+ break;
745
+ case "response.output_item.added":
746
+ processOutputItemAdded(parsed, pendingCalls);
747
+ break;
748
+ case "response.function_call_arguments.delta":
749
+ processFunctionCallArgumentsDelta(parsed, pendingCalls);
750
+ break;
751
+ case "response.function_call_arguments.done":
752
+ yield* processFunctionCallArgumentsDone(parsed, pendingCalls);
753
+ break;
754
+ case "response.completed":
755
+ yield* processCompleted(parsed);
756
+ break;
757
+ }
758
+ };
759
+ var processSSELines2 = function* (lines, state) {
760
+ for (const line of lines) {
761
+ const trimmed = line.trim();
762
+ if (!trimmed) {
763
+ if (state.currentEvent && state.buffer) {
764
+ const parsed = parseJSON(state.buffer);
765
+ if (parsed) {
766
+ yield* processSSEEvent(state.currentEvent, parsed, state.pendingCalls);
767
+ }
768
+ state.currentEvent = "";
769
+ state.buffer = "";
770
+ }
771
+ continue;
772
+ }
773
+ if (trimmed.startsWith("event: ")) {
774
+ state.currentEvent = trimmed.slice(EVENT_PREFIX_LENGTH);
775
+ } else if (trimmed.startsWith("data: ")) {
776
+ state.buffer = trimmed.slice(DATA_PREFIX_LENGTH);
777
+ }
778
+ }
779
+ };
780
+ var drainReader2 = async function* (reader, decoder, state, signal) {
781
+ let textBuffer = "";
782
+ for (let result = await reader.read();!result.done && !signal?.aborted; result = await reader.read()) {
783
+ textBuffer += decoder.decode(result.value, { stream: true });
784
+ const lines = textBuffer.split(`
785
+ `);
786
+ textBuffer = lines.pop() ?? "";
787
+ yield* processSSELines2(lines, state);
788
+ }
789
+ if (textBuffer.trim()) {
790
+ yield* processSSELines2([textBuffer, ""], state);
791
+ }
792
+ };
793
+ var parseSSEStream2 = async function* (body, signal) {
794
+ const reader = body.getReader();
795
+ const decoder = new TextDecoder;
796
+ const state = {
797
+ buffer: "",
798
+ currentEvent: "",
799
+ pendingCalls: new Map,
800
+ usage: undefined
801
+ };
802
+ try {
803
+ yield* drainReader2(reader, decoder, state, signal);
804
+ } finally {
805
+ reader.releaseLock();
806
+ }
807
+ };
808
+ var fetchResponsesStream = async function* (baseUrl, apiKey, body, signal) {
809
+ const response = await fetch(`${baseUrl}/v1/responses`, {
810
+ body: JSON.stringify(body),
811
+ headers: {
812
+ Authorization: `Bearer ${apiKey}`,
813
+ "Content-Type": "application/json"
814
+ },
815
+ method: "POST",
816
+ signal
817
+ });
818
+ if (!response.ok) {
819
+ const errorText = await response.text();
820
+ throw new Error(`OpenAI Responses API error ${response.status}: ${errorText}`);
821
+ }
822
+ if (!response.body) {
823
+ throw new Error("OpenAI Responses API returned no response body");
824
+ }
825
+ yield* parseSSEStream2(response.body, signal);
826
+ };
827
+ var openaiResponses = (config) => {
828
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL2;
829
+ return {
830
+ stream: (params) => {
831
+ const body = buildRequestBody2(params, config.imageGeneration);
832
+ return fetchResponsesStream(baseUrl, config.apiKey, body, params.signal);
833
+ }
834
+ };
835
+ };
836
+
837
+ // src/ai/providers/gemini.ts
838
+ var DEFAULT_BASE_URL3 = "https://generativelanguage.googleapis.com";
839
+ var SSE_DATA_PREFIX_LENGTH2 = 6;
840
+ var isRecord3 = (value) => typeof value === "object" && value !== null;
841
+ var isRecordArray3 = (value) => Array.isArray(value) && value.length > 0 && isRecord3(value[0]);
842
+ var mapRole = (role) => {
843
+ if (role === "assistant" || role === "system")
844
+ return "model";
845
+ return "user";
846
+ };
847
+ var mapContentBlock = (block) => {
848
+ switch (block.type) {
849
+ case "text":
850
+ return { text: block.content };
851
+ case "image":
852
+ return {
853
+ inlineData: {
854
+ data: block.source.data,
855
+ mimeType: block.source.media_type
856
+ }
857
+ };
858
+ case "document":
859
+ return {
860
+ inlineData: {
861
+ data: block.source.data,
862
+ mimeType: block.source.media_type
863
+ }
864
+ };
865
+ case "tool_use":
866
+ return {
867
+ functionCall: {
868
+ args: typeof block.input === "string" ? JSON.parse(block.input) : block.input,
869
+ name: block.name
870
+ }
871
+ };
872
+ case "tool_result":
873
+ return {
874
+ functionResponse: {
875
+ name: block.tool_use_id,
876
+ response: { result: block.content }
877
+ }
878
+ };
879
+ default:
880
+ return null;
881
+ }
882
+ };
883
+ var convertMessageContent = (content) => {
884
+ if (typeof content === "string") {
885
+ return [{ text: content }];
886
+ }
887
+ const parts = [];
888
+ for (const block of content) {
889
+ const mapped = mapContentBlock(block);
890
+ if (mapped) {
891
+ parts.push(mapped);
892
+ }
893
+ }
894
+ return parts;
895
+ };
896
+ var hasFunctionResponse = (content) => content.some((block) => block.type === "tool_result");
897
+ var convertMessages = (messages) => {
898
+ const contents = [];
899
+ for (const msg of messages) {
900
+ if (typeof msg.content !== "string" && hasFunctionResponse(msg.content)) {
901
+ const parts = convertMessageContent(msg.content);
902
+ contents.push({ parts, role: "user" });
903
+ continue;
904
+ }
905
+ contents.push({
906
+ parts: convertMessageContent(msg.content),
907
+ role: mapRole(msg.role)
908
+ });
909
+ }
910
+ return contents;
911
+ };
912
+ var mapToolDefinitions2 = (tools) => tools.map((tool) => ({
913
+ description: tool.description,
914
+ name: tool.name,
915
+ parameters: tool.input_schema
916
+ }));
917
+ var buildRequestBody3 = (params) => {
918
+ const body = {
919
+ contents: convertMessages(params.messages),
920
+ generationConfig: {
921
+ responseModalities: ["TEXT", "IMAGE"]
922
+ }
923
+ };
924
+ if (params.systemPrompt) {
925
+ body.systemInstruction = {
926
+ parts: [{ text: params.systemPrompt }]
927
+ };
928
+ }
929
+ if (params.tools && params.tools.length > 0) {
930
+ body.tools = [
931
+ { functionDeclarations: mapToolDefinitions2(params.tools) }
932
+ ];
933
+ }
934
+ return body;
935
+ };
936
+ var extractMimeFormat2 = (mimeType) => {
937
+ if (typeof mimeType !== "string") {
938
+ return "png";
939
+ }
940
+ if (mimeType.includes("jpeg") || mimeType.includes("jpg"))
941
+ return "jpeg";
942
+ if (mimeType.includes("webp"))
943
+ return "webp";
944
+ return "png";
945
+ };
946
+ var processTextPart = function* (part) {
947
+ if (typeof part.text === "string" && part.text) {
948
+ yield { content: part.text, type: "text" };
949
+ }
950
+ };
951
+ var processInlineDataPart = function* (inlineData) {
952
+ const data = typeof inlineData.data === "string" ? inlineData.data : "";
953
+ const mimeType = inlineData.mimeType;
954
+ if (!data) {
955
+ return;
956
+ }
957
+ yield {
958
+ data,
959
+ format: extractMimeFormat2(mimeType),
960
+ isPartial: false,
961
+ type: "image"
962
+ };
963
+ };
964
+ var processFunctionCallPart = function* (functionCall) {
965
+ const name = typeof functionCall.name === "string" ? functionCall.name : "";
966
+ const args = functionCall.args ?? {};
967
+ yield {
968
+ id: `gemini-${name}-${Date.now()}`,
969
+ input: args,
970
+ name,
971
+ type: "tool_use"
972
+ };
973
+ };
974
+ var processPart = function* (part) {
975
+ if ("text" in part) {
976
+ yield* processTextPart(part);
977
+ }
978
+ if (isRecord3(part.inlineData)) {
979
+ yield* processInlineDataPart(part.inlineData);
980
+ }
981
+ if (isRecord3(part.functionCall)) {
982
+ yield* processFunctionCallPart(part.functionCall);
983
+ }
984
+ };
985
+ var extractUsage3 = (parsed) => {
986
+ if (!isRecord3(parsed.usageMetadata)) {
987
+ return;
988
+ }
989
+ const { usageMetadata } = parsed;
990
+ return {
991
+ inputTokens: typeof usageMetadata.promptTokenCount === "number" ? usageMetadata.promptTokenCount : 0,
992
+ outputTokens: typeof usageMetadata.candidatesTokenCount === "number" ? usageMetadata.candidatesTokenCount : 0
993
+ };
994
+ };
995
+ var processChunk = function* (parsed, state) {
996
+ const usage = extractUsage3(parsed);
997
+ if (usage) {
998
+ state.usage = usage;
999
+ }
1000
+ if (!isRecordArray3(parsed.candidates)) {
1001
+ return;
1002
+ }
1003
+ const candidate = parsed.candidates[0];
1004
+ if (!candidate || !isRecord3(candidate.content)) {
1005
+ return;
1006
+ }
1007
+ const { content } = candidate;
1008
+ if (!isRecordArray3(content.parts)) {
1009
+ return;
1010
+ }
1011
+ for (const part of content.parts) {
1012
+ yield* processPart(part);
1013
+ }
1014
+ };
1015
+ var processSSELine2 = function* (line, state) {
1016
+ const trimmed = line.trim();
1017
+ if (!trimmed || !trimmed.startsWith("data: ")) {
1018
+ return;
1019
+ }
1020
+ const data = trimmed.slice(SSE_DATA_PREFIX_LENGTH2);
1021
+ let parsed;
1022
+ try {
1023
+ parsed = JSON.parse(data);
1024
+ } catch {
1025
+ return;
1026
+ }
1027
+ yield* processChunk(parsed, state);
1028
+ };
1029
+ var drainReader3 = async function* (reader, decoder, state, signal) {
1030
+ let textBuffer = "";
1031
+ for (let result = await reader.read();!result.done && !signal?.aborted; result = await reader.read()) {
1032
+ textBuffer += decoder.decode(result.value, { stream: true });
1033
+ const lines = textBuffer.split(`
1034
+ `);
1035
+ textBuffer = lines.pop() ?? "";
1036
+ for (const line of lines) {
1037
+ yield* processSSELine2(line, state);
1038
+ }
1039
+ }
1040
+ if (textBuffer.trim()) {
1041
+ yield* processSSELine2(textBuffer, state);
1042
+ }
1043
+ yield { type: "done", usage: state.usage };
1044
+ };
1045
+ var parseSSEStream3 = async function* (body, signal) {
1046
+ const reader = body.getReader();
1047
+ const decoder = new TextDecoder;
1048
+ const state = {
1049
+ buffer: "",
1050
+ usage: undefined
1051
+ };
1052
+ try {
1053
+ yield* drainReader3(reader, decoder, state, signal);
1054
+ } finally {
1055
+ reader.releaseLock();
1056
+ }
1057
+ };
1058
+ var fetchGeminiStream = async function* (baseUrl, apiKey, model, body, signal) {
1059
+ const url = `${baseUrl}/v1beta/models/${model}:streamGenerateContent?alt=sse&key=${apiKey}`;
1060
+ const response = await fetch(url, {
1061
+ body: JSON.stringify(body),
1062
+ headers: {
1063
+ "Content-Type": "application/json"
1064
+ },
1065
+ method: "POST",
1066
+ signal
1067
+ });
1068
+ if (!response.ok) {
1069
+ const errorText = await response.text();
1070
+ throw new Error(`Gemini API error ${response.status}: ${errorText}`);
1071
+ }
1072
+ if (!response.body) {
1073
+ throw new Error("Gemini API returned no response body");
1074
+ }
1075
+ yield* parseSSEStream3(response.body, signal);
1076
+ };
1077
+ var gemini = (config) => {
1078
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL3;
1079
+ return {
1080
+ stream: (params) => {
1081
+ const body = buildRequestBody3(params);
1082
+ return fetchGeminiStream(baseUrl, config.apiKey, params.model, body, params.signal);
1083
+ }
1084
+ };
1085
+ };
1086
+
476
1087
  // src/plugins/aiChat.ts
477
1088
  import { Elysia } from "elysia";
478
1089
 
@@ -643,6 +1254,16 @@ var extractTextContent = (chunk, onChunk) => {
643
1254
  }
644
1255
  return chunk.content;
645
1256
  };
1257
+ var sendImageMessage = async (socket, chunk, messageId, conversationId) => sendMessage(socket, {
1258
+ conversationId,
1259
+ data: chunk.data,
1260
+ format: chunk.format,
1261
+ imageId: chunk.imageId,
1262
+ isPartial: chunk.isPartial,
1263
+ messageId,
1264
+ revisedPrompt: chunk.revisedPrompt,
1265
+ type: "image"
1266
+ });
646
1267
  var sendToolRunning = async (socket, toolName, toolInput, messageId, conversationId) => sendMessage(socket, {
647
1268
  conversationId,
648
1269
  input: toolInput,
@@ -707,6 +1328,16 @@ var processToolChunk = (chunk, state, options, socket, messageId, conversationId
707
1328
  case "text":
708
1329
  handleToolChunkText(chunk, state, options, socket, messageId, conversationId);
709
1330
  break;
1331
+ case "image":
1332
+ sendImageMessage(socket, chunk, messageId, conversationId);
1333
+ options.onImage?.({
1334
+ data: chunk.data,
1335
+ format: chunk.format,
1336
+ imageId: chunk.imageId,
1337
+ isPartial: chunk.isPartial,
1338
+ revisedPrompt: chunk.revisedPrompt
1339
+ });
1340
+ break;
710
1341
  case "tool_use":
711
1342
  handleToolChunkToolUse(chunk, state);
712
1343
  hitAnotherTool = true;
@@ -857,6 +1488,16 @@ var consumeStreamChunk = async (chunk, options, socket, messages, messageId, con
857
1488
  return "";
858
1489
  case "text":
859
1490
  return processStreamTextChunk(chunk, options, socket, messageId, conversationId);
1491
+ case "image":
1492
+ await sendImageMessage(socket, chunk, messageId, conversationId);
1493
+ options.onImage?.({
1494
+ data: chunk.data,
1495
+ format: chunk.format,
1496
+ imageId: chunk.imageId,
1497
+ isPartial: chunk.isPartial,
1498
+ revisedPrompt: chunk.revisedPrompt
1499
+ });
1500
+ return "";
860
1501
  case "tool_use":
861
1502
  await processStreamToolUseChunk(chunk, socket, options, messages, messageId, conversationId, signal, fullResponse, startTime);
862
1503
  return { earlyReturn: true, fullResponse, usage: undefined };
@@ -966,14 +1607,27 @@ var aiChat = (config) => {
966
1607
  const resolvedTools = typeof config.tools === "function" ? config.tools(providerName, model) : config.tools;
967
1608
  const userMessage = attachments && attachments.length > 0 ? {
968
1609
  content: [
969
- ...attachments.map((att) => ({
970
- source: {
971
- data: att.data,
972
- media_type: att.media_type,
973
- type: "base64"
974
- },
975
- type: "image"
976
- })),
1610
+ ...attachments.map((att) => {
1611
+ if (att.media_type === "application/pdf") {
1612
+ return {
1613
+ name: att.name,
1614
+ source: {
1615
+ data: att.data,
1616
+ media_type: att.media_type,
1617
+ type: "base64"
1618
+ },
1619
+ type: "document"
1620
+ };
1621
+ }
1622
+ return {
1623
+ source: {
1624
+ data: att.data,
1625
+ media_type: att.media_type,
1626
+ type: "base64"
1627
+ },
1628
+ type: "image"
1629
+ };
1630
+ }),
977
1631
  { content, type: "text" }
978
1632
  ],
979
1633
  role: "user"
@@ -1038,17 +1692,19 @@ export {
1038
1692
  streamAI,
1039
1693
  serializeAIMessage,
1040
1694
  parseAIMessage,
1695
+ openaiResponses,
1041
1696
  openaiCompatible,
1042
1697
  moonshot,
1043
1698
  mistralai,
1044
1699
  meta,
1045
1700
  google,
1046
1701
  generateId,
1702
+ gemini,
1047
1703
  deepseek,
1048
1704
  createConversationManager,
1049
1705
  alibaba,
1050
1706
  aiChat
1051
1707
  };
1052
1708
 
1053
- //# debugId=0917CFAC1D8BB99A64756E2164756E21
1709
+ //# debugId=214AE01443FBB99E64756E2164756E21
1054
1710
  //# sourceMappingURL=index.js.map