@stacksjs/ai 0.75.73 → 0.75.75
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/drivers/anthropic/index.js +1 -1
- package/dist/drivers/bedrock/index.js +2 -2
- package/dist/drivers/ollama/index.js +2 -2
- package/dist/drivers/openai/index.js +2 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/types.d.ts +20 -1
- package/dist/utils/tools.d.ts +28 -0
- package/dist/utils/tools.js +1 -0
- package/dist/utils/vision.js +1 -1
- package/package.json +4 -4
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import{fetchWithRetry}from"../../utils/retry";import{recordUsage}from"../../utils/usage";import{normalizeMessagesForProvider}from"../../utils/vision";import{DEFAULT_ANTHROPIC_MODEL}from"../../models";const DEFAULT_MODEL=DEFAULT_ANTHROPIC_MODEL,DEFAULT_MAX_TOKENS=4096,DEFAULT_VERSION="2023-06-01",BASE_URL="https://api.anthropic.com/v1";let globalConfig=null;export function configure(config){globalConfig=config}function getConfig(config){const merged={...globalConfig,...config};if(!merged.apiKey)merged.apiKey=process.env.ANTHROPIC_API_KEY||"";return merged}export function createAnthropicDriver(config){const{apiKey,model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,anthropicVersion=DEFAULT_VERSION}=config;return{name:"Claude API",async process(command,systemPrompt,history){if(!apiKey)throw Error("Anthropic API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":apiKey,"anthropic-version":anthropicVersion},body:JSON.stringify({model,max_tokens:maxTokens,system:systemPrompt,messages:[...history,{role:"user",content:command}]})});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const data=await response.json();if(!data.content||data.content.length===0)throw Error("Claude API returned empty content");return data.content[0].text},async*stream(command,systemPrompt,history){if(!apiKey)throw Error("Anthropic API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":apiKey,"anthropic-version":anthropicVersion},body:JSON.stringify({model,max_tokens:maxTokens,system:systemPrompt,stream:!0,messages:[...history,{role:"user",content:command}]})});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";const handlePayload=function*(data){if(data==="[DONE]")return;let event;try{event=JSON.parse(data)}catch{return}if(event.type==="error"){const msg=event.error?.message??JSON.stringify(event.error??event);throw Error(`[anthropic/stream] mid-stream error: ${msg}`)}if(event.type==="content_block_delta"&&event.delta?.text)yield event.delta.text};while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
2
|
-
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: "))yield*handlePayload(line.slice(6))}if(buffer.startsWith("data: "))yield*handlePayload(buffer.slice(6))}}}export async function chat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop,system,tools,toolChoice,responseFormat}=options,normalizedMessages=normalizeMessagesForProvider(messages,"anthropic"),startedAt=Date.now(),body={model,max_tokens:maxTokens,temperature,top_p:topP,stop_sequences:stop?Array.isArray(stop)?stop:[stop]:void 0,system,messages:normalizedMessages};if(tools&&tools.length>0){body.tools=tools.map((t)=>({name:t.name,description:t.description,input_schema:t.parameters??{type:"object",properties:{}}}));if(toolChoice!==void 0)body.tool_choice=mapAnthropicToolChoice(toolChoice)}if(responseFormat&&responseFormat.type!=="text"){const outputTool=buildAnthropicJsonTool(responseFormat),existing=Array.isArray(body.tools)?body.tools:[];body.tools=[...existing,outputTool];if(toolChoice===void 0)body.tool_choice={type:"tool",name:outputTool.name}}const response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":config.apiKey,"anthropic-version":config.anthropicVersion||DEFAULT_VERSION},body:JSON.stringify(body)});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const data=await response.json();if(!data.content||data.content.length===0)throw Error("Claude API returned empty content");const
|
|
2
|
+
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: "))yield*handlePayload(line.slice(6))}if(buffer.startsWith("data: "))yield*handlePayload(buffer.slice(6))}}}export async function chat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop,system,tools,toolChoice,responseFormat}=options,normalizedMessages=normalizeMessagesForProvider(messages,"anthropic"),startedAt=Date.now(),body={model,max_tokens:maxTokens,temperature,top_p:topP,stop_sequences:stop?Array.isArray(stop)?stop:[stop]:void 0,system,messages:normalizedMessages};if(tools&&tools.length>0){body.tools=tools.map((t)=>({name:t.name,description:t.description,input_schema:t.parameters??{type:"object",properties:{}}}));if(toolChoice!==void 0)body.tool_choice=mapAnthropicToolChoice(toolChoice)}if(responseFormat&&responseFormat.type!=="text"){const outputTool=buildAnthropicJsonTool(responseFormat),existing=Array.isArray(body.tools)?body.tools:[];body.tools=[...existing,outputTool];if(toolChoice===void 0)body.tool_choice={type:"tool",name:outputTool.name}}const response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":config.apiKey,"anthropic-version":config.anthropicVersion||DEFAULT_VERSION},body:JSON.stringify(body)});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const data=await response.json();if(!data.content||data.content.length===0)throw Error("Claude API returned empty content");const outputTool=responseFormat&&responseFormat.type!=="text"?buildAnthropicJsonTool(responseFormat).name:void 0,blocks=data.content,structured=outputTool?blocks.find((b)=>b.type==="tool_use"&&b.name===outputTool):void 0,toolCalls=blocks.filter((b)=>b.type==="tool_use"&&b!==structured).map((b)=>({id:b.id??"",name:b.name??"",arguments:b.input??{}})),result={content:structured?JSON.stringify(structured.input):blocks.filter((b)=>b.type==="text").map((b)=>b.text??"").join(""),...toolCalls.length>0?{toolCalls}:{},model:data.model,usage:{promptTokens:data.usage?.input_tokens||0,completionTokens:data.usage?.output_tokens||0,totalTokens:(data.usage?.input_tokens||0)+(data.usage?.output_tokens||0)},finishReason:data.stop_reason};recordUsage({provider:"anthropic",model:data.model,promptTokens:result.usage.promptTokens,completionTokens:result.usage.completionTokens,totalTokens:result.usage.totalTokens,durationMs:Date.now()-startedAt,timestamp:Date.now()});return result}function mapAnthropicToolChoice(choice){if(choice==="auto")return{type:"auto"};if(choice==="required")return{type:"any"};if(choice==="none")return{type:"none"};return{type:"tool",name:choice.name}}function buildAnthropicJsonTool(format){if(format.type==="json_schema")return{name:format.json_schema.name,description:`Returns the result as JSON matching the '${format.json_schema.name}' schema.`,input_schema:format.json_schema.schema};return{name:"structured_output",description:"Returns the result as a JSON object.",input_schema:{type:"object",additionalProperties:!0}}}export async function*streamChat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop,system}=options,response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":config.apiKey,"anthropic-version":config.anthropicVersion||DEFAULT_VERSION},body:JSON.stringify({model,max_tokens:maxTokens,temperature,top_p:topP,stop_sequences:stop?Array.isArray(stop)?stop:[stop]:void 0,system,stream:!0,messages:normalizeMessagesForProvider(messages,"anthropic")})});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
3
3
|
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: ")){const data=line.slice(6);if(data==="[DONE]")continue;try{const event=JSON.parse(data);if(event.type==="content_block_delta"&&event.delta?.text)yield event.delta.text}catch{}}}}export async function prompt(text,options={}){return(await chat([{role:"user",content:text}],options)).content}export function estimateTokens(text){return Math.ceil(text.length/4)}export const anthropicDriver={create:createAnthropicDriver},anthropic={configure,chat,streamChat,prompt,estimateTokens,createDriver:createAnthropicDriver};export default anthropic;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{converse}from"../../utils/client-bedrock-runtime";import{recordUsage}from"../../utils/usage";export const DEFAULT_BEDROCK_MODEL="amazon.nova-lite-v1:0";let globalConfig={};export function configure(config){globalConfig={...globalConfig,...config}}function resolveConfig(){return{...globalConfig,model:globalConfig.model||process.env.BEDROCK_MODEL_ID||DEFAULT_BEDROCK_MODEL}}const IMAGE_FORMATS=new Set(["png","jpeg","gif","webp"]);function imageBlock(mediaType,data){const format=mediaType.replace(/^image\//,"").replace("jpg","jpeg");if(!IMAGE_FORMATS.has(format))throw Error(`Bedrock Converse takes png, jpeg, gif or webp images, not ${mediaType}`);return{image:{format,source:{bytes:data}}}}function contentBlock(part){if(part.type==="text")return{text:part.text??""};if(part.type==="image"&&part.source)return imageBlock(part.source.media_type,part.source.data);if(part.type==="image_url"&&part.image_url){const inline=/^data:([^;]+);base64,(.*)$/.exec(part.image_url.url);if(!inline)throw Error("Bedrock Converse takes image bytes, not a URL: pass the image as a data: URL or a base64 `image` block");return imageBlock(inline[1],inline[2])}throw Error(`Unsupported message content for Bedrock: ${part.type}`)}export function toConverseMessages(messages,system){const systemBlocks=system?[{text:system}]:[],turns=[];for(const message of messages){if(message.role==="system"){const text=typeof message.content==="string"?message.content:message.content.filter((part)=>part.type==="text").map((part)=>part.text??"").join(`
|
|
2
|
-
`);if(text)systemBlocks.push({text});continue}turns.push({role:message.role,content:typeof message.content==="string"?[{text:message.content}]:message.content.map(contentBlock)})}return{messages:turns,...systemBlocks.length>0?{system:systemBlocks}:{}}}function structuredOutputTool(format){if(format.type==="json_schema")return{name:format.json_schema.name,description:`Returns the result as JSON matching the '${format.json_schema.name}' schema.`,inputSchema:{json:format.json_schema.schema}};return{name:"structured_output",description:"Returns the result as a JSON object.",inputSchema:{json:{type:"object",additionalProperties:!0}}}}function toolConfig(options){const{tools=[],toolChoice,responseFormat}=options,specs=toolChoice==="none"?[]:tools.map((tool)=>({name:tool.name,description:tool.description,inputSchema:{json:tool.parameters??{type:"object",properties:{}}}}));let choice;if(toolChoice==="required")choice={any:{}};else if(toolChoice&&typeof toolChoice==="object")choice={tool:{name:toolChoice.name}};else if(toolChoice==="auto")choice={auto:{}};if(responseFormat&&responseFormat.type!=="text"){const output=structuredOutputTool(responseFormat);specs.push(output);choice=choice??{tool:{name:output.name}}}if(specs.length===0)return;return{tools:specs.map((toolSpec)=>({toolSpec})),...choice?{toolChoice:choice}:{}}}function
|
|
1
|
+
import{converse}from"../../utils/client-bedrock-runtime";import{recordUsage}from"../../utils/usage";export const DEFAULT_BEDROCK_MODEL="amazon.nova-lite-v1:0";let globalConfig={};export function configure(config){globalConfig={...globalConfig,...config}}function resolveConfig(){return{...globalConfig,model:globalConfig.model||process.env.BEDROCK_MODEL_ID||DEFAULT_BEDROCK_MODEL}}const IMAGE_FORMATS=new Set(["png","jpeg","gif","webp"]);function imageBlock(mediaType,data){const format=mediaType.replace(/^image\//,"").replace("jpg","jpeg");if(!IMAGE_FORMATS.has(format))throw Error(`Bedrock Converse takes png, jpeg, gif or webp images, not ${mediaType}`);return{image:{format,source:{bytes:data}}}}function contentBlock(part){if(part.type==="text")return{text:part.text??""};if(part.type==="tool_call"&&part.toolCall)return{toolUse:{toolUseId:part.toolCall.id,name:part.toolCall.name,input:part.toolCall.arguments}};if(part.type==="tool_result"&&part.toolResult)return{toolResult:{toolUseId:part.toolResult.toolCallId,content:[{text:part.toolResult.content}],...part.toolResult.isError?{status:"error"}:{}}};if(part.type==="image"&&part.source)return imageBlock(part.source.media_type,part.source.data);if(part.type==="image_url"&&part.image_url){const inline=/^data:([^;]+);base64,(.*)$/.exec(part.image_url.url);if(!inline)throw Error("Bedrock Converse takes image bytes, not a URL: pass the image as a data: URL or a base64 `image` block");return imageBlock(inline[1],inline[2])}throw Error(`Unsupported message content for Bedrock: ${part.type}`)}export function toConverseMessages(messages,system){const systemBlocks=system?[{text:system}]:[],turns=[];for(const message of messages){if(message.role==="system"){const text=typeof message.content==="string"?message.content:message.content.filter((part)=>part.type==="text").map((part)=>part.text??"").join(`
|
|
2
|
+
`);if(text)systemBlocks.push({text});continue}turns.push({role:message.role,content:typeof message.content==="string"?[{text:message.content}]:message.content.map(contentBlock)})}return{messages:turns,...systemBlocks.length>0?{system:systemBlocks}:{}}}function structuredOutputTool(format){if(format.type==="json_schema")return{name:format.json_schema.name,description:`Returns the result as JSON matching the '${format.json_schema.name}' schema.`,inputSchema:{json:format.json_schema.schema}};return{name:"structured_output",description:"Returns the result as a JSON object.",inputSchema:{json:{type:"object",additionalProperties:!0}}}}function toolConfig(options){const{tools=[],toolChoice,responseFormat}=options,specs=toolChoice==="none"?[]:tools.map((tool)=>({name:tool.name,description:tool.description,inputSchema:{json:tool.parameters??{type:"object",properties:{}}}}));let choice;if(toolChoice==="required")choice={any:{}};else if(toolChoice&&typeof toolChoice==="object")choice={tool:{name:toolChoice.name}};else if(toolChoice==="auto")choice={auto:{}};if(responseFormat&&responseFormat.type!=="text"){const output=structuredOutputTool(responseFormat);specs.push(output);choice=choice??{tool:{name:output.name}}}if(specs.length===0)return;return{tools:specs.map((toolSpec)=>({toolSpec})),...choice?{toolChoice:choice}:{}}}function readOutput(output,outputTool){const blocks=output.output.message?.content??[],uses=blocks.filter((block)=>("toolUse"in block)),structured=outputTool?uses.find((block)=>block.toolUse.name===outputTool):void 0,toolCalls=uses.filter((block)=>block!==structured).map((block)=>({id:block.toolUse.toolUseId,name:block.toolUse.name,arguments:block.toolUse.input??{}}));return{content:structured?JSON.stringify(structured.toolUse.input):blocks.filter((block)=>("text"in block)).map((block)=>block.text).join(""),...toolCalls.length>0?{toolCalls}:{}}}export async function chat(messages,options={}){const config=resolveConfig(),model=options.model||config.model,startedAt=Date.now(),inferenceConfig={},maxTokens=options.maxTokens??config.maxTokens;if(maxTokens!==void 0)inferenceConfig.maxTokens=maxTokens;if(options.temperature!==void 0)inferenceConfig.temperature=options.temperature;if(options.topP!==void 0)inferenceConfig.topP=options.topP;if(options.stop!==void 0)inferenceConfig.stopSequences=Array.isArray(options.stop)?options.stop:[options.stop];const tools=toolConfig(options),response=await converse({modelId:model,...toConverseMessages(messages,options.system),...Object.keys(inferenceConfig).length>0?{inferenceConfig}:{},...tools?{toolConfig:tools}:{}},config.region),outputTool=options.responseFormat&&options.responseFormat.type!=="text"?structuredOutputTool(options.responseFormat).name:void 0,result={...readOutput(response,outputTool),model,usage:{promptTokens:response.usage?.inputTokens??0,completionTokens:response.usage?.outputTokens??0,totalTokens:response.usage?.totalTokens??0},finishReason:response.stopReason};recordUsage({provider:"bedrock",model,promptTokens:result.usage.promptTokens,completionTokens:result.usage.completionTokens,totalTokens:result.usage.totalTokens,durationMs:Date.now()-startedAt,timestamp:Date.now()});return result}export const bedrock={configure,chat};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const DEFAULT_HOST="http://localhost:11434",DEFAULT_MODEL="llama3.2",DEFAULT_EMBEDDING_MODEL="nomic-embed-text";let globalConfig={};export function configure(config){globalConfig={...globalConfig,...config}}function getConfig(config){return{host:config?.host||globalConfig.host||process.env.OLLAMA_HOST||DEFAULT_HOST,model:config?.model||globalConfig.model||process.env.OLLAMA_MODEL||DEFAULT_MODEL,embeddingModel:config?.embeddingModel||globalConfig.embeddingModel||DEFAULT_EMBEDDING_MODEL}}export function createOllamaDriver(config={}){const{host=process.env.OLLAMA_HOST||DEFAULT_HOST,model=process.env.OLLAMA_MODEL||DEFAULT_MODEL,embeddingModel=DEFAULT_EMBEDDING_MODEL}=config;return{name:"Ollama",async process(command,systemPrompt,history){const response=await fetch(`${host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}],stream:!1})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}return(await response.json()).message.content},async*stream(command,systemPrompt,history){const response=await fetch(`${host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}],stream:!0})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
2
|
-
`);buffer=lines.pop()||"";for(const line of lines){if(!line.trim())continue;try{const data=JSON.parse(line);if(data.message?.content)yield data.message.content}catch{}}}},async embed(input){const inputs=Array.isArray(input)?input:[input],embeddings=[];for(const text of inputs){const response=await fetch(`${host}/api/embeddings`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:embeddingModel,prompt:text})});if(!response.ok){const error=await response.text();throw Error(`Ollama Embeddings API error: ${error}`)}const data=await response.json();embeddings.push(data.embedding)}return Array.isArray(input)?embeddings:embeddings[0]}}}export async function chat(messages,options={}){const config=getConfig(),{model=config.model,temperature,topP,stop,responseFormat}=options,format=responseFormat?.type==="json_schema"?responseFormat.json_schema.schema:responseFormat?.type==="json_object"?"json":void 0,response=await fetch(`${config.host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages,stream:!1,format,options:{temperature,top_p:topP,stop}})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const data=await response.json();return{content:data.message.
|
|
1
|
+
import{parseToolArguments,toOllamaMessages}from"../../utils/tools";const DEFAULT_HOST="http://localhost:11434",DEFAULT_MODEL="llama3.2",DEFAULT_EMBEDDING_MODEL="nomic-embed-text";let globalConfig={};export function configure(config){globalConfig={...globalConfig,...config}}function getConfig(config){return{host:config?.host||globalConfig.host||process.env.OLLAMA_HOST||DEFAULT_HOST,model:config?.model||globalConfig.model||process.env.OLLAMA_MODEL||DEFAULT_MODEL,embeddingModel:config?.embeddingModel||globalConfig.embeddingModel||DEFAULT_EMBEDDING_MODEL}}export function createOllamaDriver(config={}){const{host=process.env.OLLAMA_HOST||DEFAULT_HOST,model=process.env.OLLAMA_MODEL||DEFAULT_MODEL,embeddingModel=DEFAULT_EMBEDDING_MODEL}=config;return{name:"Ollama",async process(command,systemPrompt,history){const response=await fetch(`${host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}],stream:!1})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}return(await response.json()).message.content},async*stream(command,systemPrompt,history){const response=await fetch(`${host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}],stream:!0})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
2
|
+
`);buffer=lines.pop()||"";for(const line of lines){if(!line.trim())continue;try{const data=JSON.parse(line);if(data.message?.content)yield data.message.content}catch{}}}},async embed(input){const inputs=Array.isArray(input)?input:[input],embeddings=[];for(const text of inputs){const response=await fetch(`${host}/api/embeddings`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:embeddingModel,prompt:text})});if(!response.ok){const error=await response.text();throw Error(`Ollama Embeddings API error: ${error}`)}const data=await response.json();embeddings.push(data.embedding)}return Array.isArray(input)?embeddings:embeddings[0]}}}export async function chat(messages,options={}){const config=getConfig(),{model=config.model,temperature,topP,stop,responseFormat,tools,toolChoice}=options;if(toolChoice==="required"||toolChoice&&typeof toolChoice==="object")throw Error("Ollama cannot be made to call a tool: offer the tools with toolChoice 'auto', or use another driver");const offered=toolChoice==="none"?[]:tools??[],format=responseFormat?.type==="json_schema"?responseFormat.json_schema.schema:responseFormat?.type==="json_object"?"json":void 0,response=await fetch(`${config.host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:toOllamaMessages(messages),stream:!1,format,...offered.length>0?{tools:offered.map((tool)=>({type:"function",function:{name:tool.name,description:tool.description,parameters:tool.parameters??{type:"object",properties:{}}}}))}:{},options:{temperature,top_p:topP,stop}})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const data=await response.json(),toolCalls=(data.message?.tool_calls??[]).map((call,index)=>({id:call.id??`call_${index}`,name:call.function.name,arguments:parseToolArguments(call.function.arguments,call.function.name)}));return{content:data.message?.content??"",...toolCalls.length>0?{toolCalls}:{},model:data.model,usage:{promptTokens:data.prompt_eval_count||0,completionTokens:data.eval_count||0,totalTokens:(data.prompt_eval_count||0)+(data.eval_count||0)},finishReason:data.done_reason||"stop"}}export async function*streamChat(messages,options={}){const config=getConfig(),{model=config.model,temperature,topP,stop}=options,response=await fetch(`${config.host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:toOllamaMessages(messages),stream:!0,options:{temperature,top_p:topP,stop}})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
3
3
|
`);buffer=lines.pop()||"";for(const line of lines){if(!line.trim())continue;try{const data=JSON.parse(line);if(data.message?.content)yield data.message.content}catch{}}}}export async function generate(prompt,options={}){const config=getConfig(),response=await fetch(`${config.host}/api/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:options.model||config.model,prompt,system:options.system,template:options.template,context:options.context,raw:options.raw,format:options.format,images:options.images,stream:!1})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const data=await response.json();return{content:data.response,model:data.model,usage:{promptTokens:data.prompt_eval_count||0,completionTokens:data.eval_count||0,totalTokens:(data.prompt_eval_count||0)+(data.eval_count||0)}}}export async function embed(input,model){const config=getConfig(),embeddingModel=model||config.embeddingModel,inputs=Array.isArray(input)?input:[input],embeddings=[];for(const text of inputs){const response=await fetch(`${config.host}/api/embeddings`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:embeddingModel,prompt:text})});if(!response.ok){const error=await response.text();throw Error(`Ollama Embeddings API error: ${error}`)}const data=await response.json();embeddings.push(data.embedding)}return Array.isArray(input)?embeddings:embeddings[0]}export async function listModels(){const config=getConfig(),response=await fetch(`${config.host}/api/tags`,{method:"GET"});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}return(await response.json()).models}export async function pullModel(name,onProgress){const config=getConfig(),response=await fetch(`${config.host}/api/pull`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name,stream:!0})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const reader=response.body?.getReader();if(!reader)return;const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
4
4
|
`);buffer=lines.pop()||"";for(const line of lines){if(!line.trim())continue;try{const data=JSON.parse(line);if(onProgress)onProgress(data.status??"",data.completed,data.total)}catch{}}}}export async function deleteModel(name){const config=getConfig(),response=await fetch(`${config.host}/api/delete`,{method:"DELETE",headers:{"Content-Type":"application/json"},body:JSON.stringify({name})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}}export async function showModel(_name){const config=getConfig(),response=await fetch(`${config.host}/api/show`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:_name})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}return response.json()}export async function isRunning(){const config=getConfig();try{return(await fetch(`${config.host}/api/tags`,{method:"GET"})).ok}catch{return!1}}export const ollamaDriver={create:createOllamaDriver},ollama={configure,chat,streamChat,generate,embed,listModels,pullModel,deleteModel,showModel,isRunning,createDriver:createOllamaDriver};export default ollama;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{fetchWithRetry}from"../../utils/retry";import{
|
|
2
|
-
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: "))yield*handlePayload(line.slice(6))}if(buffer.startsWith("data: "))yield*handlePayload(buffer.slice(6))},async embed(input){if(!apiKey)throw Error("OpenAI API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${baseUrl}/embeddings`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${apiKey}`},body:JSON.stringify({model:embeddingModel,input})});if(!response.ok){const error=await response.text();throw Error(`OpenAI Embeddings API error: ${error}`)}const data=await response.json();if(Array.isArray(input))return data.data.map((d)=>d.embedding);return data.data[0].embedding}}}export async function chat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop,tools,toolChoice,responseFormat}=options,normalizedMessages=
|
|
1
|
+
import{fetchWithRetry}from"../../utils/retry";import{parseToolArguments,toChatCompletionMessages}from"../../utils/tools";import{recordUsage}from"../../utils/usage";const DEFAULT_MODEL="gpt-4o",DEFAULT_MAX_TOKENS=4096,DEFAULT_EMBEDDING_MODEL="text-embedding-3-small",DEFAULT_BASE_URL="https://api.openai.com/v1";let globalConfig=null;export function configure(config){globalConfig=config}function getConfig(config){const merged={...globalConfig,...config};if(!merged.apiKey)merged.apiKey=process.env.OPENAI_API_KEY||"";return merged}export function createOpenAIDriver(config){const{apiKey,model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,baseUrl=DEFAULT_BASE_URL,embeddingModel=DEFAULT_EMBEDDING_MODEL}=config;return{name:"OpenAI",async process(command,systemPrompt,history){if(!apiKey)throw Error("OpenAI API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${baseUrl}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${apiKey}`},body:JSON.stringify({model,max_tokens:maxTokens,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}]})});if(!response.ok){const error=await response.text();throw Error(`OpenAI API error: ${error}`)}const data=await response.json();if(!data.choices||data.choices.length===0)throw Error("OpenAI API returned empty choices");return data.choices[0].message.content},async*stream(command,systemPrompt,history){if(!apiKey)throw Error("OpenAI API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${baseUrl}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${apiKey}`},body:JSON.stringify({model,max_tokens:maxTokens,stream:!0,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}]})});if(!response.ok){const error=await response.text();throw Error(`OpenAI API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";const handlePayload=function*(data){if(data==="[DONE]")return;let parsed;try{parsed=JSON.parse(data)}catch{return}if(parsed?.error){const msg=parsed.error.message||JSON.stringify(parsed.error);throw Error(`[openai/stream] mid-stream error: ${msg}`)}const content=parsed.choices?.[0]?.delta?.content;if(content)yield content};while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
2
|
+
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: "))yield*handlePayload(line.slice(6))}if(buffer.startsWith("data: "))yield*handlePayload(buffer.slice(6))},async embed(input){if(!apiKey)throw Error("OpenAI API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${baseUrl}/embeddings`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${apiKey}`},body:JSON.stringify({model:embeddingModel,input})});if(!response.ok){const error=await response.text();throw Error(`OpenAI Embeddings API error: ${error}`)}const data=await response.json();if(Array.isArray(input))return data.data.map((d)=>d.embedding);return data.data[0].embedding}}}export async function chat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop,tools,toolChoice,responseFormat}=options,normalizedMessages=toChatCompletionMessages(messages,{argumentsAsString:!0}),startedAt=Date.now(),body={model,max_tokens:maxTokens,temperature,top_p:topP,stop,messages:normalizedMessages};if(tools?.length)body.tools=tools.map((tool)=>({type:"function",function:{name:tool.name,description:tool.description,parameters:tool.parameters??{type:"object",properties:{}}}}));if(toolChoice!==void 0)body.tool_choice=typeof toolChoice==="object"?{type:"function",function:{name:toolChoice.name}}:toolChoice;if(responseFormat&&responseFormat.type!=="text")body.response_format=responseFormat;const response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify(body)});if(!response.ok){const error=await response.text();throw Error(`OpenAI API error: ${error}`)}const data=await response.json();if(!data.choices||data.choices.length===0)throw Error("OpenAI API returned empty choices");const message=data.choices[0].message,toolCalls=(message.tool_calls??[]).map((call)=>({id:call.id,name:call.function.name,arguments:parseToolArguments(call.function.arguments,call.function.name)})),result={content:message.content??"",...toolCalls.length>0?{toolCalls}:{},model:data.model,usage:{promptTokens:data.usage?.prompt_tokens||0,completionTokens:data.usage?.completion_tokens||0,totalTokens:data.usage?.total_tokens||0},finishReason:data.choices[0].finish_reason};recordUsage({provider:"openai",model:data.model,promptTokens:result.usage.promptTokens,completionTokens:result.usage.completionTokens,totalTokens:result.usage.totalTokens,durationMs:Date.now()-startedAt,timestamp:Date.now()});return result}export async function*streamChat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop}=options,response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify({model,max_tokens:maxTokens,temperature,top_p:topP,stop,stream:!0,messages:toChatCompletionMessages(messages,{argumentsAsString:!0})})});if(!response.ok){const error=await response.text();throw Error(`OpenAI API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
3
3
|
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: ")){const data=line.slice(6);if(data==="[DONE]")continue;try{const content=JSON.parse(data).choices[0]?.delta?.content;if(content)yield content}catch{}}}}export async function embed(input,model=DEFAULT_EMBEDDING_MODEL){const config=getConfig(),response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/embeddings`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify({model,input})});if(!response.ok){const error=await response.text();throw Error(`OpenAI Embeddings API error: ${error}`)}const data=await response.json();if(Array.isArray(input))return data.data.map((d)=>d.embedding);return data.data[0].embedding}export async function generateImage(prompt,options={}){const config=getConfig(),{model="dall-e-3",size="1024x1024",quality="standard",n=1,responseFormat="url"}=options,response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/images/generations`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify({model,prompt,size,quality,n,response_format:responseFormat})});if(!response.ok){const error=await response.text();throw Error(`OpenAI Image API error: ${error}`)}return(await response.json()).data}export async function transcribe(audioFile,options={}){const config=getConfig(),formData=new FormData;formData.append("file",audioFile);formData.append("model",options.model||"whisper-1");if(options.language)formData.append("language",options.language);if(options.prompt)formData.append("prompt",options.prompt);if(options.responseFormat)formData.append("response_format",options.responseFormat);if(options.temperature!==void 0)formData.append("temperature",String(options.temperature));const response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/audio/transcriptions`,{method:"POST",headers:{Authorization:`Bearer ${config.apiKey}`},body:formData});if(!response.ok){const error=await response.text();throw Error(`OpenAI Whisper API error: ${error}`)}return response.json()}export async function textToSpeech(input,options={}){const config=getConfig(),{model="tts-1",voice="alloy",responseFormat="mp3",speed=1}=options,response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/audio/speech`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify({model,input,voice,response_format:responseFormat,speed})});if(!response.ok){const error=await response.text();throw Error(`OpenAI TTS API error: ${error}`)}return response.arrayBuffer()}export const openaiDriver={create:createOpenAIDriver},openai={configure,chat,streamChat,embed,generateImage,transcribe,textToSpeech,createDriver:createOpenAIDriver};export default openai;
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,9 @@ export * from './utils/client-bedrock-runtime';
|
|
|
35
35
|
// translates between the OpenAI image_url and Anthropic image
|
|
36
36
|
// source formats so apps can switch providers without rewriting.
|
|
37
37
|
export { buildMessageWithImages, normalizeMessagesForProvider } from './utils/vision';
|
|
38
|
+
// Tool round trips across drivers: `AIResult.toolCalls` in, then the
|
|
39
|
+
// assistant's turn and the results back out.
|
|
40
|
+
export { assistantTurn, toolResultsTurn } from './utils/tools';
|
|
38
41
|
// HTTP retry helper for 429/5xx (stacksjs/stacks#1878 A-5).
|
|
39
42
|
export { fetchWithRetry } from './utils/retry';
|
|
40
43
|
// Usage tracking (stacksjs/stacks#1878 A-6). Apps install reporters
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./types";export*from"./drivers";export*from"./client";export*from"./agents";export*from"./buddy";export*from"./context";export*from"./text";export*from"./image";export*from"./speech";export*from"./search";export*from"./personalization";export*from"./mcp";export*from"./models";export*from"./utils/client-bedrock";export*from"./utils/client-bedrock-runtime";export{buildMessageWithImages,normalizeMessagesForProvider}from"./utils/vision";export{fetchWithRetry}from"./utils/retry";export{clearUsageReporters,listUsageReporters,onUsage,recordUsage}from"./utils/usage";export{estimateMessageTokens,estimateTokens,sanitizePrompt}from"./utils/tokens";export*from"./utils/model-access";
|
|
1
|
+
export*from"./types";export*from"./drivers";export*from"./client";export*from"./agents";export*from"./buddy";export*from"./context";export*from"./text";export*from"./image";export*from"./speech";export*from"./search";export*from"./personalization";export*from"./mcp";export*from"./models";export*from"./utils/client-bedrock";export*from"./utils/client-bedrock-runtime";export{buildMessageWithImages,normalizeMessagesForProvider}from"./utils/vision";export{assistantTurn,toolResultsTurn}from"./utils/tools";export{fetchWithRetry}from"./utils/retry";export{clearUsageReporters,listUsageReporters,onUsage,recordUsage}from"./utils/usage";export{estimateMessageTokens,estimateTokens,sanitizePrompt}from"./utils/tokens";export*from"./utils/model-access";
|
package/dist/types.d.ts
CHANGED
|
@@ -8,10 +8,28 @@ export declare interface AIMessage {
|
|
|
8
8
|
content: string | AIMessageContent[]
|
|
9
9
|
}
|
|
10
10
|
export declare interface AIMessageContent {
|
|
11
|
-
type: 'text' | 'image_url' | 'image'
|
|
11
|
+
type: 'text' | 'image_url' | 'image' | 'tool_call' | 'tool_result'
|
|
12
12
|
text?: string
|
|
13
13
|
image_url?: { url: string, detail?: 'auto' | 'low' | 'high' }
|
|
14
14
|
source?: { type: 'base64', media_type: string, data: string }
|
|
15
|
+
toolCall?: AIToolCall
|
|
16
|
+
toolResult?: AIToolResult
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A tool the model asked to call. The same shape from every driver: OpenAI's
|
|
20
|
+
* `tool_calls`, Anthropic's `tool_use`, Bedrock's `toolUse`, Ollama's
|
|
21
|
+
* `tool_calls`.
|
|
22
|
+
*/
|
|
23
|
+
export declare interface AIToolCall {
|
|
24
|
+
id: string
|
|
25
|
+
name: string
|
|
26
|
+
arguments: Record<string, unknown>
|
|
27
|
+
}
|
|
28
|
+
export declare interface AIToolResult {
|
|
29
|
+
toolCallId: string
|
|
30
|
+
name?: string
|
|
31
|
+
content: string
|
|
32
|
+
isError?: boolean
|
|
15
33
|
}
|
|
16
34
|
export declare interface AIDriver {
|
|
17
35
|
name: string
|
|
@@ -65,6 +83,7 @@ export declare interface ChatCompletionOptions {
|
|
|
65
83
|
}
|
|
66
84
|
export declare interface AIResult {
|
|
67
85
|
content: string
|
|
86
|
+
toolCalls?: AIToolCall[]
|
|
68
87
|
model: string
|
|
69
88
|
usage?: {
|
|
70
89
|
promptTokens: number
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AIMessage, AIResult, AIToolResult } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* The assistant message that records `result`, to send back with the results.
|
|
4
|
+
*/
|
|
5
|
+
export declare function assistantTurn(result: Pick<AIResult, 'content' | 'toolCalls'>): AIMessage;
|
|
6
|
+
/**
|
|
7
|
+
* The user message that answers the calls of the previous assistant turn.
|
|
8
|
+
*/
|
|
9
|
+
export declare function toolResultsTurn(results: AIToolResult[]): AIMessage;
|
|
10
|
+
/**
|
|
11
|
+
* Parse a tool call's arguments. OpenAI sends them as a JSON string, the
|
|
12
|
+
* others as an object; a string that is not a JSON object is a model error,
|
|
13
|
+
* reported rather than swallowed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseToolArguments(raw: unknown, tool: string): Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Messages in the OpenAI chat-completions shape, which Ollama shares: an
|
|
18
|
+
* assistant's calls ride on the message as `tool_calls`, and each result is a
|
|
19
|
+
* message of its own with role `tool`. Everything else is normalised as before.
|
|
20
|
+
*/
|
|
21
|
+
export declare function toChatCompletionMessages(messages: AIMessage[], options: { argumentsAsString: boolean }): Array<Record<string, unknown>>;
|
|
22
|
+
/**
|
|
23
|
+
* Messages in Ollama's `/api/chat` shape: `content` is a string, images ride
|
|
24
|
+
* in `images` as bare base64, a call's arguments are an object, and a result
|
|
25
|
+
* names its tool. Content arrays used to be sent as they were, which Ollama
|
|
26
|
+
* rejects.
|
|
27
|
+
*/
|
|
28
|
+
export declare function toOllamaMessages(messages: AIMessage[]): Array<Record<string, unknown>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{normalizeMessagesForProvider}from"./vision";export function assistantTurn(result){if(!result.toolCalls?.length)return{role:"assistant",content:result.content};const content=result.content?[{type:"text",text:result.content}]:[];for(const call of result.toolCalls)content.push({type:"tool_call",toolCall:call});return{role:"assistant",content}}export function toolResultsTurn(results){return{role:"user",content:results.map((toolResult)=>({type:"tool_result",toolResult}))}}export function parseToolArguments(raw,tool){if(raw===void 0||raw===null||raw==="")return{};if(typeof raw!=="string")return raw;try{const parsed=JSON.parse(raw);if(parsed&&typeof parsed==="object"&&!Array.isArray(parsed))return parsed}catch{}throw Error(`The model called "${tool}" with arguments that are not a JSON object: ${raw.slice(0,200)}`)}function blocksOf(message){return typeof message.content==="string"?[{type:"text",text:message.content}]:message.content}function textOf(blocks){return blocks.filter((block)=>block.type==="text").map((block)=>block.text??"").join("")}export function toChatCompletionMessages(messages,options){const out=[];for(const message of messages){const blocks=blocksOf(message),calls=blocks.filter((block)=>block.type==="tool_call"&&block.toolCall).map((block)=>block.toolCall),results=blocks.filter((block)=>block.type==="tool_result"&&block.toolResult).map((block)=>block.toolResult);if(calls.length===0&&results.length===0){out.push(normalizeMessagesForProvider([message],"openai")[0]);continue}if(calls.length>0)out.push({role:"assistant",content:textOf(blocks)||null,tool_calls:calls.map((call)=>({id:call.id,type:"function",function:{name:call.name,arguments:options.argumentsAsString?JSON.stringify(call.arguments):call.arguments}}))});for(const result of results)out.push({role:"tool",tool_call_id:result.toolCallId,...result.name?{tool_name:result.name}:{},content:result.content});const remaining=blocks.filter((block)=>block.type!=="tool_call"&&block.type!=="tool_result");if(calls.length===0&&remaining.length>0)out.push(normalizeMessagesForProvider([{role:message.role,content:remaining}],"openai")[0])}return out}export function toOllamaMessages(messages){return toChatCompletionMessages(messages,{argumentsAsString:!1}).map((message)=>{if(message.role==="tool")return{role:"tool",content:message.content,...message.tool_name?{tool_name:message.tool_name}:{}};const out={role:message.role};if(Array.isArray(message.content)){const parts=message.content;out.content=parts.filter((part)=>part.type==="text").map((part)=>part.text??"").join("");const images=parts.filter((part)=>part.type==="image_url").map((part)=>{const inline=/^data:[^;]+;base64,(.*)$/.exec(part.image_url?.url??"");if(!inline)throw Error("Ollama takes image bytes, not a URL: pass the image as a data: URL or a base64 `image` block");return inline[1]});if(images.length>0)out.images=images}else out.content=message.content??"";if(message.tool_calls)out.tool_calls=message.tool_calls.map((call)=>({function:call.function}));return out})}
|
package/dist/utils/vision.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function toOpenAIContent(block){if(block.type==="text")return{type:"text",text:block.text??""};if(block.type==="image_url")return{type:"image_url",image_url:block.image_url};if(block.type==="image"&&block.source){if(block.source.type==="base64")return{type:"image_url",image_url:{url:`data:${block.source.media_type};base64,${block.source.data}`}}}return block}function toAnthropicContent(block){if(block.type==="text")return{type:"text",text:block.text??""};if(block.type==="image"&&block.source)return{type:"image",source:block.source};if(block.type==="image_url"&&block.image_url){const url=block.image_url.url,dataMatch=url.match(/^data:([^;]+);base64,(.+)$/);if(dataMatch)return{type:"image",source:{type:"base64",media_type:dataMatch[1],data:dataMatch[2]}};return{type:"image",source:{type:"url",url}}}return block}export function normalizeMessagesForProvider(messages,provider){return messages.map((msg)=>{if(typeof msg.content==="string")return msg;const mapper=provider==="openai"?toOpenAIContent:toAnthropicContent;return{role:msg.role,content:msg.content.map(mapper)}})}export function buildMessageWithImages(command,images){const blocks=[];for(const img of images)if(img.dataBase64&&img.mediaType)blocks.push({type:"image",source:{type:"base64",media_type:img.mediaType,data:img.dataBase64}});else if(img.url)blocks.push({type:"image_url",image_url:{url:img.url,detail:img.detail}});blocks.push({type:"text",text:command});return blocks}
|
|
1
|
+
function toOpenAIContent(block){if(block.type==="text")return{type:"text",text:block.text??""};if(block.type==="image_url")return{type:"image_url",image_url:block.image_url};if(block.type==="image"&&block.source){if(block.source.type==="base64")return{type:"image_url",image_url:{url:`data:${block.source.media_type};base64,${block.source.data}`}}}return block}function toAnthropicContent(block){if(block.type==="text")return{type:"text",text:block.text??""};if(block.type==="tool_call"&&block.toolCall)return{type:"tool_use",id:block.toolCall.id,name:block.toolCall.name,input:block.toolCall.arguments};if(block.type==="tool_result"&&block.toolResult)return{type:"tool_result",tool_use_id:block.toolResult.toolCallId,content:block.toolResult.content,...block.toolResult.isError?{is_error:!0}:{}};if(block.type==="image"&&block.source)return{type:"image",source:block.source};if(block.type==="image_url"&&block.image_url){const url=block.image_url.url,dataMatch=url.match(/^data:([^;]+);base64,(.+)$/);if(dataMatch)return{type:"image",source:{type:"base64",media_type:dataMatch[1],data:dataMatch[2]}};return{type:"image",source:{type:"url",url}}}return block}export function normalizeMessagesForProvider(messages,provider){return messages.map((msg)=>{if(typeof msg.content==="string")return msg;const mapper=provider==="openai"?toOpenAIContent:toAnthropicContent;return{role:msg.role,content:msg.content.map(mapper)}})}export function buildMessageWithImages(command,images){const blocks=[];for(const img of images)if(img.dataBase64&&img.mediaType)blocks.push({type:"image",source:{type:"base64",media_type:img.mediaType,data:img.dataBase64}});else if(img.url)blocks.push({type:"image_url",image_url:{url:img.url,detail:img.detail}});blocks.push({type:"text",text:command});return blocks}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/ai",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.75",
|
|
6
6
|
"description": "Stacks Artificial Intelligence.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"prepublishOnly": "bun run build"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@stacksjs/cli": "0.75.
|
|
69
|
-
"@stacksjs/config": "0.75.
|
|
70
|
-
"@stacksjs/storage": "0.75.
|
|
68
|
+
"@stacksjs/cli": "0.75.75",
|
|
69
|
+
"@stacksjs/config": "0.75.75",
|
|
70
|
+
"@stacksjs/storage": "0.75.75",
|
|
71
71
|
"@stacksjs/ts-cloud": "^0.16.39"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|