@urun-sh/openai 0.2.49 → 0.2.51
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/ResponsesClient-CQ3l8Wft.d.cts +62 -0
- package/dist/ResponsesClient-CQ3l8Wft.d.ts +56 -0
- package/dist/chunk-FSS2A7IU.js +1 -0
- package/dist/chunk-KJ25VEMG.js +21 -0
- package/dist/chunk-OLE2YJO3.js +1 -0
- package/dist/chunk-PS4BLX7J.js +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +6 -64
- package/dist/index.d.ts +6 -57
- package/dist/index.js +1 -1
- package/dist/pi-extension/index.cjs +3 -0
- package/dist/pi-extension/index.d.cts +189 -0
- package/dist/pi-extension/index.d.ts +70 -0
- package/dist/pi-extension/index.js +3 -0
- package/dist/proxy/cli.cjs +21 -15
- package/dist/proxy/cli.js +11 -5
- package/dist/proxy/index.cjs +11 -11
- package/dist/proxy/index.d.cts +32 -1
- package/dist/proxy/index.d.ts +12 -1
- package/dist/proxy/index.js +1 -1
- package/package.json +12 -2
- package/dist/chunk-GUQC3NSG.js +0 -1
- package/dist/chunk-YDXG2QGW.js +0 -21
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** The narrow subset of @urun-sh/core's Session the SDK depends on. */
|
|
2
|
+
interface UrunDocLike {
|
|
3
|
+
set(patch: Record<string, unknown>): void;
|
|
4
|
+
get(path?: string, defaultValue?: unknown): unknown;
|
|
5
|
+
on(event: 'change', handler: (snapshot: Record<string, unknown>) => void): () => void;
|
|
6
|
+
}
|
|
7
|
+
interface UrunStreamLike {
|
|
8
|
+
messages(): AsyncIterable<unknown>;
|
|
9
|
+
attach(track: unknown): Promise<void>;
|
|
10
|
+
detach(): Promise<void>;
|
|
11
|
+
readonly track: unknown;
|
|
12
|
+
/**
|
|
13
|
+
* Subscribe to the named media track (de)appearing. The downstream audio track
|
|
14
|
+
* is consumed ASYNCHRONOUSLY after the WebRTC handshake, so `track` may be null
|
|
15
|
+
* at `enableAudio()` time; this fires with the real `MediaStreamTrack` the
|
|
16
|
+
* moment the consumer attaches (core `SessionStream.on('track')`). Optional so
|
|
17
|
+
* non-media test doubles need not implement it.
|
|
18
|
+
*/
|
|
19
|
+
on?(event: 'track', handler: (track: unknown) => void): () => void;
|
|
20
|
+
}
|
|
21
|
+
interface UrunSessionLike {
|
|
22
|
+
/** Per-leg consumer identity (core: transport.consumerId). */
|
|
23
|
+
readonly consumerId: string;
|
|
24
|
+
doc(key: string): UrunDocLike;
|
|
25
|
+
stream(name: string): UrunStreamLike;
|
|
26
|
+
close?(): Promise<void> | void;
|
|
27
|
+
}
|
|
28
|
+
/** Lane message shape emitted by the serve runtime (core session.ts:83). */
|
|
29
|
+
type LaneMessage = {
|
|
30
|
+
t: 'delta';
|
|
31
|
+
request_id?: string;
|
|
32
|
+
delta: string;
|
|
33
|
+
} | {
|
|
34
|
+
t: 'response';
|
|
35
|
+
request_id?: string;
|
|
36
|
+
body: unknown;
|
|
37
|
+
} | {
|
|
38
|
+
t: 'error';
|
|
39
|
+
request_id?: string;
|
|
40
|
+
body: unknown;
|
|
41
|
+
code?: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
interface ResponsesStream extends AsyncIterable<unknown> {
|
|
45
|
+
readonly requestId: string;
|
|
46
|
+
}
|
|
47
|
+
declare class UrunResponses {
|
|
48
|
+
private readonly transport;
|
|
49
|
+
constructor(session: UrunSessionLike);
|
|
50
|
+
readonly responses: {
|
|
51
|
+
create: (params: {
|
|
52
|
+
model?: string;
|
|
53
|
+
input: unknown;
|
|
54
|
+
stream?: boolean;
|
|
55
|
+
tools?: unknown;
|
|
56
|
+
temperature?: number;
|
|
57
|
+
max_output_tokens?: number;
|
|
58
|
+
}) => Promise<ResponsesStream>;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { type LaneMessage as L, UrunResponses as U, type UrunSessionLike as a };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
interface UrunDocLike {
|
|
3
|
+
set(patch: Record<string, unknown>): void;
|
|
4
|
+
get(path?: string, defaultValue?: unknown): unknown;
|
|
5
|
+
on(event: 'change', handler: (snapshot: Record<string, unknown>) => void): () => void;
|
|
6
|
+
}
|
|
7
|
+
interface UrunStreamLike {
|
|
8
|
+
messages(): AsyncIterable<unknown>;
|
|
9
|
+
attach(track: unknown): Promise<void>;
|
|
10
|
+
detach(): Promise<void>;
|
|
11
|
+
readonly track: unknown;
|
|
12
|
+
|
|
13
|
+
on?(event: 'track', handler: (track: unknown) => void): () => void;
|
|
14
|
+
}
|
|
15
|
+
interface UrunSessionLike {
|
|
16
|
+
|
|
17
|
+
readonly consumerId: string;
|
|
18
|
+
doc(key: string): UrunDocLike;
|
|
19
|
+
stream(name: string): UrunStreamLike;
|
|
20
|
+
close?(): Promise<void> | void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type LaneMessage = {
|
|
24
|
+
t: 'delta';
|
|
25
|
+
request_id?: string;
|
|
26
|
+
delta: string;
|
|
27
|
+
} | {
|
|
28
|
+
t: 'response';
|
|
29
|
+
request_id?: string;
|
|
30
|
+
body: unknown;
|
|
31
|
+
} | {
|
|
32
|
+
t: 'error';
|
|
33
|
+
request_id?: string;
|
|
34
|
+
body: unknown;
|
|
35
|
+
code?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
interface ResponsesStream extends AsyncIterable<unknown> {
|
|
39
|
+
readonly requestId: string;
|
|
40
|
+
}
|
|
41
|
+
declare class UrunResponses {
|
|
42
|
+
private readonly transport;
|
|
43
|
+
constructor(session: UrunSessionLike);
|
|
44
|
+
readonly responses: {
|
|
45
|
+
create: (params: {
|
|
46
|
+
model?: string;
|
|
47
|
+
input: unknown;
|
|
48
|
+
stream?: boolean;
|
|
49
|
+
tools?: unknown;
|
|
50
|
+
temperature?: number;
|
|
51
|
+
max_output_tokens?: number;
|
|
52
|
+
}) => Promise<ResponsesStream>;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { type LaneMessage as L, UrunResponses as U, type UrunSessionLike as a };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function m(e){if(e instanceof Error)return{type:"error",error:{type:"urun_error",code:e.name||null,message:e.message}};if(e&&typeof e=="object"&&e.t==="error"){let n=e,t=n.body??{};return{type:"error",error:{type:"urun_error",code:n.code??null,message:t.message??"unknown error"}}}return{type:"error",error:{type:"urun_error",code:null,message:String(e)}}}function c(e,n,t,r){let s=e.response??{},o={request_id:t,consumer_id:r,stream:!0,kind:"chat",messages:n};return typeof s.instructions=="string"&&(o.instructions=s.instructions),Array.isArray(s.modalities)&&(o.modalities=s.modalities),typeof s.temperature=="number"&&(o.temperature=s.temperature),typeof s.max_output_tokens=="number"&&(o.max_output_tokens=s.max_output_tokens),s.tools!==void 0&&(o.tools=s.tools),o}function d(e,n,t){let r={request_id:n,consumer_id:t,stream:!!e.stream,kind:"responses",input:e.input};return e.model&&(r.model=e.model),e.tools!==void 0&&(r.tools=e.tools),typeof e.temperature=="number"&&(r.temperature=e.temperature),typeof e.max_output_tokens=="number"&&(r.max_output_tokens=e.max_output_tokens),r}var g="llm-resp";async function*a(e,n){let t=`${g}:${n}`,r=`resp_${n}`;yield{type:"response.created",response:{id:r,status:"in_progress"}};for await(let s of e.stream(t).messages()){let o=s;if(o.t==="delta")yield{type:"response.output_text.delta",item_id:r,delta:o.delta};else if(o.t==="response"){yield{type:"response.completed",response:{id:r,status:"completed",...o.body}};return}else if(o.t==="error"){yield m(o);return}}}var _="llm",p=class{constructor(n){this.session=n}session;get consumerId(){return this.session.consumerId}write(n){this.session.doc(_).set({requests:{[n.request_id]:{payload:n,consumer_id:n.consumer_id,stream:n.stream}}})}sendResponses(n,t){let r=d(n,t,this.consumerId);return this.write(r),a(this.session,t)}sendResponseCreate(n,t,r){let s=c(n,t,r,this.consumerId);return this.write(s),a(this.session,r)}};var l=0;function k(){return l+=1,`req_${Date.now().toString(36)}_${l.toString(36)}`}var y=class{transport;constructor(n){this.transport=new p(n)}responses={create:async n=>{let t=k(),r=this.transport.sendResponses(n,t);return Object.assign((async function*(){yield*r})(),{requestId:t})}}};export{m as a,p as b,y as c};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import{b as I}from"./chunk-PS4BLX7J.js";import{createServer as H}from"http";var T=class e{startedAt=Date.now();requests={};models={};ttftMs=[];tokRates=[];tokensOut=0;charsOut=0;localInBytes=0;naiveInBytes=0;request(n,t,r){this.requests[n]=(this.requests[n]??0)+1,this.localInBytes+=t,this.naiveInBytes+=r}modelEntry(n){let t=this.models[n];if(t)return t;let r=Object.keys(this.models).length>=Q?"(other)":n;return this.models[r]??={requests:0,tokens_out:0}}model(n){this.modelEntry(n).requests+=1}track(n,t){let r=this,i=performance.now(),c=null,u=0;return(async function*(){for await(let p of n)p.type==="response.output_text.delta"&&typeof p.delta=="string"&&(c===null&&(c=performance.now(),r.ttftMs.push(c-i),r.ttftMs.length>512&&r.ttftMs.shift()),u+=1,r.tokensOut+=1,r.charsOut+=p.delta.length,t!==void 0&&(r.modelEntry(t).tokens_out+=1)),yield p;let o=(performance.now()-(c??i))/1e3;u>0&&o>0&&(r.tokRates.push(u/o),r.tokRates.length>512&&r.tokRates.shift())})()}static p50(n){if(!n.length)return null;let t=[...n].sort((r,i)=>r-i);return t[Math.floor(t.length/2)]}snapshot(){let n=Object.values(this.requests).reduce((t,r)=>t+r,0);return{uptime_s:Math.round((Date.now()-this.startedAt)/1e3),requests:this.requests,models:this.models,tokens_out:this.tokensOut,chars_out:this.charsOut,ttft_ms:{p50:e.p50(this.ttftMs),last:this.ttftMs.at(-1)??null},tok_per_s:{p50:e.p50(this.tokRates),last:this.tokRates.at(-1)??null},traffic:{local_request_bytes:this.localInBytes,naive_baseline:{request_bytes:this.naiveInBytes,connections:n},persistent_connections:1,history_bytes_saved:Math.max(0,this.naiveInBytes-this.localInBytes),history_bytes_saved_pct:this.naiveInBytes>0?Math.round((1-this.localInBytes/this.naiveInBytes)*1e3)/10:null}}}};function C(e){e.writeHead(200,{"content-type":"text/event-stream","cache-control":"no-cache",connection:"keep-alive"})}function w(e,n,t){e.writeHead(n,{"content-type":"application/json"}),e.end(JSON.stringify(t))}function _(e,n,t,r="invalid_request_error"){w(e,n,{error:{message:t,type:r}})}function j(e,n,t){if(n==="anthropic"){w(e,404,{type:"error",error:{type:"not_found_error",message:t.message}});return}w(e,404,{error:{message:t.message,type:"invalid_request_error",code:"model_not_found"}})}var Q=256,V=128;function D(e){return typeof e.model!="string"||!e.model.trim()?"(default)":e.model.trim().slice(0,V)}var z=64*1024*1024,B=class extends Error{};async function W(e){let n=[],t=0;for await(let i of e){if(t+=i.length,t>z)throw new B(`request body exceeds ${z} bytes`);n.push(i)}let r=Buffer.concat(n).toString("utf8");return r?JSON.parse(r):{}}function $(e,n,t,r){return{id:e,object:"chat.completion.chunk",created:Math.floor(Date.now()/1e3),model:n,choices:[{index:0,delta:t,finish_reason:r}]}}var Z=256;function tt(e){if(typeof e=="string")return[{role:"user",content:e}];if(Array.isArray(e))return e;throw new Error("responses input must be a string or an array of messages/items")}var q=class{conversations=new Map;thread(n,t){let r=tt(t);if(n==null)return r;let i=this.conversations.get(String(n));if(!i)throw new Error(`unknown previous_response_id ${String(n)} (proxy restarts drop stored responses)`);return[...i,...r]}remember(n,t,r){this.conversations.set(n,[...t,{role:"assistant",content:r}]);for(let i of this.conversations.keys()){if(this.conversations.size<=Z)break;this.conversations.delete(i)}}};function K(e){let n=e?.output;return Array.isArray(n)?n.filter(t=>t.type==="function_call"):[]}function M(e){return e.find(n=>n.type==="response.completed")?.response??null}function et(e){if(Array.isArray(e))return e.map(n=>{let t=n.function;return!t||typeof t!="object"?n:{type:"function",name:t.name,description:t.description,parameters:t.parameters,strict:t.strict}})}var J=class extends Error{};function nt(e,n){return Array.isArray(n)?n.map(t=>{let r=t;if(r.type==="text")return{type:e==="assistant"?"output_text":"input_text",text:r.text};if(r.type==="image_url"){let i=r.image_url??{};return{type:"input_image",image_url:i.url,detail:i.detail}}throw new J(`unsupported chat content part type "${String(r.type)}" \u2014 the proxy translates text and image_url parts`)}):n}function rt(e){let n=[];for(let t of e){if(t.role==="tool"){n.push({type:"function_call_output",call_id:t.tool_call_id,output:typeof t.content=="string"?t.content:JSON.stringify(t.content??"")});continue}if(t.role==="assistant"&&Array.isArray(t.tool_calls)){typeof t.content=="string"&&t.content&&n.push({role:"assistant",content:t.content});for(let r of t.tool_calls){let i=r.function??{};n.push({type:"function_call",call_id:r.id,name:i.name,arguments:i.arguments})}continue}n.push({...t,content:nt(t.role,t.content)})}return n}function F(e){return K(e).map((n,t)=>({index:t,id:n.call_id??n.id??`call_${t}`,type:"function",function:{name:n.name??"",arguments:n.arguments??"{}"}}))}function U(e,n){let t=n?.output_text;return typeof t=="string"?t:e.filter(r=>r.type==="response.output_text.delta"&&typeof r.delta=="string").map(r=>r.delta).join("")}async function st(e,n,t,r,i){let c=e.stream===!0,u;try{u=t.thread(e.previous_response_id,e.input)}catch(s){_(r,400,String(s instanceof Error?s.message:s));return}e.previous_response_id!=null&&(i.naiveInBytes+=Buffer.byteLength(JSON.stringify(u))-Buffer.byteLength(JSON.stringify(e.input)));let o=`resp_${Math.random().toString(36).slice(2,14)}`,p=e.store!==!1,y=D(e);i.model(y);let l;try{l=await n.createResponse({model:e.model,input:u,stream:c,tools:e.tools,temperature:e.temperature,max_output_tokens:e.max_output_tokens})}catch(s){if(s instanceof I){j(r,"openai",s);return}throw s}let g=[];if(c){C(r);let s=d=>{r.write(`event: ${String(d.type??"message")}
|
|
2
|
+
data: ${JSON.stringify(d)}
|
|
3
|
+
|
|
4
|
+
`)},a=d=>d.response&&typeof d.response=="object"?{...d,response:{...d.response,id:o}}:d,m=`item_${o.slice(5)}`,R=!1,x=!1,f=!1,A="",S=d=>{f&&(f=!1,s({type:"response.output_text.done",item_id:m,output_index:0,content_index:0,text:A}),s({type:"response.content_part.done",item_id:m,output_index:0,content_index:0,part:{type:"output_text",text:A}}),s({type:"response.output_item.done",output_index:0,item:{id:m,type:"message",role:"assistant",status:d,content:[{type:"output_text",text:A}]}}))};for await(let d of i.track(l,y)){g.push(d);let O=String(d.type??"");if(O==="response.created"||O==="response.in_progress"){R=!0,s(a(d));continue}if(O==="response.output_item.added"){x=!0,s(a(d));continue}if(O==="response.output_text.delta"&&!x){R||(R=!0,s({type:"response.created",response:{id:o,object:"response",status:"in_progress"}})),f||(f=!0,s({type:"response.output_item.added",output_index:0,item:{id:m,type:"message",role:"assistant",status:"in_progress",content:[]}}),s({type:"response.content_part.added",item_id:m,output_index:0,content_index:0,part:{type:"output_text",text:""}})),A+=String(d.delta??""),s({type:O,item_id:m,output_index:0,content_index:0,delta:d.delta});continue}if(O==="response.completed"&&!x){let Y=f;S("completed"),K(d.response).forEach((E,G)=>{let b=(Y?1:0)+G,N=E.id??E.call_id??`fc_${b}`,P=E.arguments??"",L={id:N,type:"function_call",call_id:E.call_id??N,name:E.name??""};s({type:"response.output_item.added",output_index:b,item:{...L,arguments:"",status:"in_progress"}}),s({type:"response.function_call_arguments.delta",item_id:N,output_index:b,delta:P}),s({type:"response.function_call_arguments.done",item_id:N,output_index:b,arguments:P}),s({type:"response.output_item.done",output_index:b,item:{...L,arguments:P,status:"completed"}})}),s(a(d));continue}s(a(d))}S("incomplete"),p&&t.remember(o,u,U(g,M(g))),r.write(`data: [DONE]
|
|
5
|
+
|
|
6
|
+
`),r.end();return}let h=null;for await(let s of i.track(l,y)){if(g.push(s),s.type==="error"){_(r,502,JSON.stringify(s),"upstream_error");return}s.type==="response.completed"&&(h=s.response)}if(h==null){_(r,502,"upstream produced no response.completed event","upstream_error");return}let k=U(g,h);p&&t.remember(o,u,k),w(r,200,{...h,id:o})}async function ot(e,n,t,r){let i=e.messages;if(!Array.isArray(i)){_(t,400,"chat/completions requires a messages array");return}let c=String(e.model??"urun"),u=`chatcmpl-${Math.random().toString(36).slice(2,14)}`,o;try{o=rt(i)}catch(s){if(s instanceof J){_(t,400,s.message);return}throw s}let p=D(e);r.model(p);let y;try{y=await n.createResponse({model:e.model,input:o,stream:!0,tools:et(e.tools),temperature:e.temperature,max_output_tokens:e.max_completion_tokens??e.max_tokens})}catch(s){if(s instanceof I){j(t,"openai",s);return}throw s}if(e.stream===!0){C(t);let s=[];t.write(`data: ${JSON.stringify($(u,c,{role:"assistant"},null))}
|
|
7
|
+
|
|
8
|
+
`);for await(let m of r.track(y,p))if(s.push(m),m.type==="response.output_text.delta"&&typeof m.delta=="string")t.write(`data: ${JSON.stringify($(u,c,{content:m.delta},null))}
|
|
9
|
+
|
|
10
|
+
`);else if(m.type==="error"){t.write(`data: ${JSON.stringify({error:m})}
|
|
11
|
+
|
|
12
|
+
`),t.end();return}let a=F(M(s));a.length>0&&t.write(`data: ${JSON.stringify($(u,c,{tool_calls:a},null))}
|
|
13
|
+
|
|
14
|
+
`),t.write(`data: ${JSON.stringify($(u,c,{},a.length>0?"tool_calls":"stop"))}
|
|
15
|
+
|
|
16
|
+
`),t.write(`data: [DONE]
|
|
17
|
+
|
|
18
|
+
`),t.end();return}let l="",g=[];for await(let s of r.track(y,p))if(g.push(s),s.type==="response.output_text.delta"&&typeof s.delta=="string"&&(l+=s.delta),s.type==="error"){_(t,502,JSON.stringify(s),"upstream_error");return}let h=F(M(g)).map(({index:s,...a})=>a),k={role:"assistant",content:l||null};h.length>0&&(k.tool_calls=h),w(t,200,{id:u,object:"chat.completion",created:Math.floor(Date.now()/1e3),model:c,choices:[{index:0,message:k,finish_reason:h.length>0?"tool_calls":"stop"}],usage:{prompt_tokens:0,completion_tokens:0,total_tokens:0}})}function it(e){return typeof e=="string"?e:Array.isArray(e)?e.filter(n=>n.type==="text").map(n=>String(n.text??"")).join(""):""}var v=class extends Error{constructor(n){super(`unsupported anthropic content block type "${n}" \u2014 the proxy translates text, tool_use and tool_result blocks`)}};function at(e){if(Array.isArray(e))return e.map(n=>{let t=n;return{type:"function",name:t.name,description:t.description,parameters:t.input_schema}})}function ut(e){if(typeof e=="string")return e;if(e==null)return"";if(!Array.isArray(e))throw new v(typeof e);let n="";for(let t of e){let r=String(t.type??"");if(r!=="text")throw new v(`tool_result > ${r}`);n+=String(t.text??"")}return n}function ct(e){let{role:n,content:t}=e;if(typeof t=="string")return[{role:n,content:t}];if(t==null)return[];if(!Array.isArray(t))throw new v(typeof t);let r=[],i="",c=()=>{i&&(r.push({role:n,content:i}),i="")};for(let u of t){let o=String(u.type??"");if(o==="text")i+=String(u.text??"");else if(o==="tool_use")c(),r.push({type:"function_call",call_id:u.id,name:u.name,arguments:JSON.stringify(u.input??{})});else if(o==="tool_result")c(),r.push({type:"function_call_output",call_id:u.tool_use_id,output:ut(u.content)});else{if(o==="thinking"||o==="redacted_thinking")continue;throw new v(o)}}return c(),r}function X(e){return K(e).map((n,t)=>{let r;try{r=JSON.parse(n.arguments||"{}")}catch{throw new Error(`upstream function_call "${n.name??""}" arguments are not valid JSON`)}return{type:"tool_use",id:n.call_id??n.id??`toolu_${t}`,name:n.name??"",input:r}})}async function pt(e,n,t,r){let i=e.messages;if(!Array.isArray(i)){_(t,400,"messages requires a messages array");return}let c=String(e.model??"urun"),u=`msg_${Math.random().toString(36).slice(2,14)}`,o=[];try{e.system&&o.push({role:"system",content:it(e.system)});for(let a of i)o.push(...ct(a))}catch(a){if(a instanceof v){_(t,400,a.message);return}throw a}let p=D(e);r.model(p);let y;try{y=await n.createResponse({model:e.model,input:o,stream:!0,tools:at(e.tools),temperature:e.temperature,max_output_tokens:e.max_tokens})}catch(a){if(a instanceof I){j(t,"anthropic",a);return}throw a}let l=(a,m)=>{t.write(`event: ${a}
|
|
19
|
+
data: ${JSON.stringify({type:a,...m})}
|
|
20
|
+
|
|
21
|
+
`)};if(e.stream===!0){let a=r.track(y,p);C(t),l("message_start",{message:{id:u,type:"message",role:"assistant",content:[],model:c,stop_reason:null,usage:{input_tokens:0,output_tokens:0}}}),l("content_block_start",{index:0,content_block:{type:"text",text:""}});let m=0,R=[];for await(let f of a)if(R.push(f),f.type==="response.output_text.delta"&&typeof f.delta=="string")m+=1,l("content_block_delta",{index:0,delta:{type:"text_delta",text:f.delta}});else if(f.type==="error"){l("error",{error:{type:"api_error",message:JSON.stringify(f)}}),t.end();return}let x;try{x=X(M(R))}catch(f){l("error",{error:{type:"api_error",message:String(f instanceof Error?f.message:f)}}),t.end();return}l("content_block_stop",{index:0}),x.forEach((f,A)=>{let S=1+A;l("content_block_start",{index:S,content_block:{...f,input:{}}}),l("content_block_delta",{index:S,delta:{type:"input_json_delta",partial_json:JSON.stringify(f.input??{})}}),l("content_block_stop",{index:S})}),l("message_delta",{delta:{stop_reason:x.length>0?"tool_use":"end_turn"},usage:{output_tokens:m}}),l("message_stop",{}),t.end();return}let g="",h=[];for await(let a of r.track(y,p))if(h.push(a),a.type==="response.output_text.delta"&&typeof a.delta=="string"&&(g+=a.delta),a.type==="error"){_(t,502,JSON.stringify(a),"upstream_error");return}let k;try{k=X(M(h))}catch(a){_(t,502,String(a instanceof Error?a.message:a),"upstream_error");return}let s=[];(g||k.length===0)&&s.push({type:"text",text:g}),s.push(...k),w(t,200,{id:u,type:"message",role:"assistant",content:s,model:c,stop_reason:k.length>0?"tool_use":"end_turn",usage:{input_tokens:0,output_tokens:0}})}function yt(e){let{clients:n,apiKey:t,identity:r}=e,i=new q,c=new T;return H((u,o)=>{(async()=>{let p=new URL(u.url??"/","http://localhost");if(u.method==="GET"&&p.pathname==="/healthz"){w(o,200,{ok:!0});return}if(u.method==="GET"&&p.pathname==="/stats"){w(o,200,r?{identity:r,...c.snapshot()}:c.snapshot());return}if(t&&(u.headers.authorization??"")!==`Bearer ${t}`){_(o,401,"invalid local proxy api key","authentication_error");return}if(u.method==="GET"&&p.pathname==="/v1/models"){w(o,200,await n.listModels());return}if(u.method!=="POST"){_(o,404,`no route for ${u.method} ${p.pathname}`);return}let y;try{y=await W(u)}catch(g){if(g instanceof B){_(o,413,g.message,"request_too_large");return}_(o,400,"request body is not valid JSON");return}let l=Buffer.byteLength(JSON.stringify(y));if(p.pathname==="/v1/responses"){c.request("responses",l,l),await st(y,n,i,o,c);return}if(p.pathname==="/v1/chat/completions"){c.request("chat_completions",l,l),await ot(y,n,o,c);return}if(p.pathname==="/v1/messages"){c.request("messages",l,l),await pt(y,n,o,c);return}_(o,404,`no route for POST ${p.pathname}`)})().catch(p=>{o.headersSent?o.end():_(o,500,String(p),"proxy_error")})})}export{yt as a};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
async function a(t){let e=await(t.fetchImpl??fetch)(`${t.catalogUrl}/model_catalog?select=model_id,variant`,{headers:{apikey:t.anonKey,Authorization:`Bearer ${t.anonKey}`}});if(!e.ok)throw new Error(`model_catalog fetch failed: ${e.status}`);return await e.json()}async function i(t){return{object:"list",data:(await a(t)).map(e=>({id:`${e.model_id}:${e.variant}`,object:"model",created:0,owned_by:"urun"}))}}export{a,i as b};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var g="https://api.urun.sh/v1";function l(a){return a.toLowerCase().replace(/[^\p{L}\p{N}-]/gu,"-").replace(/^-+|-+$/g,"")}var r=class extends Error{};async function m(a){let e=a.fetchImpl??fetch,t=`${a.apiUrl.replace(/\/+$/,"")}/apps`,s=await e(t,{headers:{Authorization:`Bearer ${a.apiKey}`,Accept:"application/json"}});if(!s.ok)throw new Error(`org apps listing failed: GET ${t} \u2192 ${s.status}`);let i=await s.json();if(!Array.isArray(i.apps))throw new Error(`org apps listing returned no "apps" array (GET ${t})`);if(i.truncated===!0)throw new Error(`org apps listing was truncated (GET ${t} returned ${i.apps.length} of more) \u2014 model discovery would silently omit deployed apps`);return i.apps}var c=6e4,u=class{constructor(e){this.opts=e}opts;pool=new Map;appsCache=null;seed(e,t){this.pool.set(e,Promise.resolve(t))}async deployedApps(){if(!this.opts.listApps)return[];let e=this.opts.appsTtlMs??c;if(this.appsCache&&Date.now()-this.appsCache.at<e)return this.appsCache.apps;let t=await this.opts.listApps();return this.appsCache={at:Date.now(),apps:t},t}servable(e){return e.filter(t=>t.function_name===this.opts.fnName&&t.deployment_status==="active").map(t=>t.app_slug)}async availableIds(){let e=new Set([this.opts.defaultApp]);if(this.opts.listApps)for(let t of this.servable(await this.deployedApps()))e.add(t);for(let t of this.pool.keys())e.add(t);return[this.opts.defaultApp,...[...e].filter(t=>t!==this.opts.defaultApp).sort()]}async resolveApp(e){let t=(e??"").trim();if(!t||t==="urun")return this.opts.defaultApp;let s=l(t);if(s===this.opts.defaultApp)return this.opts.defaultApp;if(this.pool.has(s))return s;if(this.opts.listApps){let i=await this.deployedApps(),p=this.servable(i);if(p.includes(s))return s;let o=p.filter(n=>n.startsWith(`${s}-`));if(o.length===1)return o[0];if(o.length>1)throw new r(`model "${t}" is ambiguous across deployed apps ${o.join(", ")} \u2014 name the exact app slug (or catalog id:variant)`);let d=i.find(n=>n.app_slug===s||n.app_slug.startsWith(`${s}-`));if(d)throw new r(`model "${t}" maps to app "${d.app_slug}", which is not an active "${this.opts.fnName}" app \u2014 deploy it with \`urun serve ${t}\`; available models: ${(await this.availableIds()).join(", ")}`)}if(this.opts.listCatalog){let p=(await this.opts.listCatalog()).find(o=>t===o.model_id||t===`${o.model_id}:${o.variant}`||s===l(o.model_id)||s===l(`${o.model_id}-${o.variant}`));if(p)throw new r(`model "${t}" is in the uRun catalog but not deployed \u2014 deploy it with \`urun serve ${p.model_id}\`; available models: ${(await this.availableIds()).join(", ")}`)}return this.opts.defaultApp}async sessionFor(e){let t=await this.resolveApp(e),s=this.pool.get(t);return s||(s=Promise.resolve(this.opts.openSession(t)),this.pool.set(t,s),s.catch(()=>this.pool.delete(t))),{app:t,entry:await s}}async modelList(){return{object:"list",data:(await this.availableIds()).map(t=>({id:t,object:"model",created:0,owned_by:"urun"}))}}async closeAll(){let e=[...this.pool.values()];this.pool.clear();let t=[];if(await Promise.all(e.map(async s=>{try{await this.opts.closeSession(await s)}catch(i){t.push(String(i instanceof Error?i.message:i))}})),t.length>0)throw new Error(`failed to close ${t.length} pooled session(s): ${t.join("; ")}`)}};export{g as a,r as b,m as c,u as d};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var D=Object.create;var f=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var N=(n,e)=>{for(var t in e)f(n,t,{get:e[t],enumerable:!0})},b=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of $(e))!H.call(n,o)&&o!==t&&f(n,o,{get:()=>e[o],enumerable:!(r=q(e,o))||r.enumerable});return n};var v=(n,e,t)=>(t=n!=null?D(j(n)):{},b(e||!n||!n.__esModule?f(t,"default",{value:n,enumerable:!0}):t,n)),W=n=>b(f({},"__esModule",{value:!0}),n);var Q={};N(Q,{SUPPORTED_REALTIME_CLIENT_EVENTS:()=>S,SUPPORTED_RESPONSES_EVENTS:()=>_,UrunRealtime:()=>y,UrunResponses:()=>w,isSupportedClientEvent:()=>P,isSupportedResponsesEvent:()=>O,listModels:()=>L,nodeAudioBackend:()=>x,toErrorEvent:()=>d,weriftAudioBackend:()=>R});module.exports=W(Q);function E(n,e,t,r){let o=n.response??{},s={request_id:t,consumer_id:r,stream:!0,kind:"chat",messages:e};return typeof o.instructions=="string"&&(s.instructions=o.instructions),Array.isArray(o.modalities)&&(s.modalities=o.modalities),typeof o.temperature=="number"&&(s.temperature=o.temperature),typeof o.max_output_tokens=="number"&&(s.max_output_tokens=o.max_output_tokens),o.tools!==void 0&&(s.tools=o.tools),s}function A(n,e,t){let r={request_id:e,consumer_id:t,stream:!!n.stream,kind:"responses",input:n.input};return n.model&&(r.model=n.model),n.tools!==void 0&&(r.tools=n.tools),typeof n.temperature=="number"&&(r.temperature=n.temperature),typeof n.max_output_tokens=="number"&&(r.max_output_tokens=n.max_output_tokens),r}function d(n){if(n instanceof Error)return{type:"error",error:{type:"urun_error",code:n.name||null,message:n.message}};if(n&&typeof n=="object"&&n.t==="error"){let e=n,t=e.body??{};return{type:"error",error:{type:"urun_error",code:e.code??null,message:t.message??"unknown error"}}}return{type:"error",error:{type:"urun_error",code:null,message:String(n)}}}var F="llm-resp";async function*h(n,e){let t=`${F}:${e}`,r=`resp_${e}`;yield{type:"response.created",response:{id:r,status:"in_progress"}};for await(let o of n.stream(t).messages()){let s=o;if(s.t==="delta")yield{type:"response.output_text.delta",item_id:r,delta:s.delta};else if(s.t==="response"){yield{type:"response.completed",response:{id:r,status:"completed",...s.body}};return}else if(s.t==="error"){yield d(s);return}}}var K="llm",p=class{constructor(e){this.session=e}session;get consumerId(){return this.session.consumerId}write(e){this.session.doc(K).set({requests:{[e.request_id]:{payload:e,consumer_id:e.consumer_id,stream:e.stream}}})}sendResponses(e,t){let r=A(e,t,this.consumerId);return this.write(r),h(this.session,t)}sendResponseCreate(e,t,r){let o=E(e,t,r,this.consumerId);return this.write(o),h(this.session,r)}};var k=class{constructor(e,t){this.backend=e;this.opts=t}backend;opts;source=null;sink=null;outHandlers=new Set;async startOutbound(){return this.source=this.backend.createSource(),this.source.track}appendInputAudio(e){if(!this.source)throw new Error("startOutbound() not called");let t=Buffer.from(e,"base64"),r=Math.floor(t.byteLength/2),o=new Int16Array(r);for(let s=0;s<r;s++)o[s]=t.readInt16LE(s*2);this.source.onData(o)}async startInbound(e){if(e==null)throw new Error('startInbound: no downstream audio track \u2014 the runtime audio producer has not been consumed yet (session.stream("rt-audio-out").track is null)');this.sink=this.backend.createSink(e),this.sink.onframe=t=>{let r=Buffer.from(t.samples.buffer,t.samples.byteOffset,t.samples.byteLength).toString("base64");for(let o of this.outHandlers)o(r)}}onOutputAudio(e){return this.outHandlers.add(e),()=>this.outHandlers.delete(e)}},V=24e3,m=480,Y=111;async function z(){let n=await import("opusscript"),e=n.default??n;return new e(V,1,2048)}async function R(){let n=await import("werift"),e=await z();return{createSource:()=>{let t=new n.MediaStreamTrack({kind:"audio"}),r=Math.random()*4294967295>>>0,o=Math.random()*65535&65535,s=Math.random()*4294967295>>>0,i=new Int16Array(0);return{track:t,onData:u=>{let a=new Int16Array(i.length+u.length);a.set(i,0),a.set(u,i.length);let l=0;for(;a.length-l>=m;){let g=a.subarray(l,l+m);l+=m;let B=Buffer.from(g.buffer,g.byteOffset,g.byteLength),C=e.encode(B,m);o=o+1&65535,s=s+m>>>0;let M=new n.RtpHeader({version:2,payloadType:Y,sequenceNumber:o,timestamp:s,ssrc:r,marker:!1}),U=new n.RtpPacket(M,C);t.writeRtp(U)}i=a.slice(l)}}},createSink:t=>{let r={onframe:null};return t.onReceiveRtp.subscribe(s=>{let i;try{i=e.decode(s.payload)}catch{return}let u=new Int16Array(i.length/2);for(let a=0;a<u.length;a++)u[a]=i.readInt16LE(a*2);r.onframe?.({samples:u})}),r}}}async function x(){let n=await import("@roamhq/wrtc");return{createSource:()=>{let e=new n.nonstandard.RTCAudioSource;return{track:e.createTrack(),onData:r=>e.onData({samples:r,sampleRate:24e3})}},createSink:e=>{let t={onframe:null},r=new n.nonstandard.RTCAudioSink(e);return r.ondata=o=>{t.onframe?.(o)},t}}}var I=0;function X(){return I+=1,`rt_${Date.now().toString(36)}_${I.toString(36)}`}var y=class{constructor(e,t={}){this.session=e;this.opts=t;this.transport=new p(e)}session;opts;transport;handlers=new Set;conversation=[];active=null;audio=null;currentRequestId=null;async enableAudio(){if(!this.opts.audioBackend)throw new Error("audioBackend required for audio");this.audio=new k(this.opts.audioBackend,{sampleRate:24e3});let e=await this.audio.startOutbound();await this.session.stream("rt-audio-in").attach(e),this.audio.onOutputAudio(r=>this.emit({type:"response.audio.delta",delta:r}));let t=await this.awaitDownstreamAudioTrack();await this.audio.startInbound(t)}awaitDownstreamAudioTrack(e=1e4){let t=this.session.stream("rt-audio-out");return t.track!=null?Promise.resolve(t.track):new Promise((r,o)=>{let s,i=setTimeout(()=>{s?.(),o(new Error(`enableAudio: no downstream audio track within ${e}ms (runtime produced no voice OUT / SFU audio consumer never attached)`))},e),u=a=>{a!=null&&(clearTimeout(i),s?.(),r(a))};s=t.on?.("track",u),t.track!=null&&u(t.track)})}on(e,t){return this.handlers.add(t),()=>this.handlers.delete(t)}emit(e){for(let t of this.handlers)t(e)}send(e){switch(e.type){case"conversation.item.create":{let t=(e.item?.content??[]).map(r=>r.text??"").join("");this.conversation.push({role:e.item?.role??"user",content:t});return}case"response.create":{let t=X();this.currentRequestId=t;let r=this.transport.sendResponseCreate({type:"response.create",response:e.response},this.conversation,t);this.active=(async()=>{try{for await(let o of r)this.emit(o)}catch(o){this.emit(d(o))}})();return}case"input_audio_buffer.append":this.audio?.appendInputAudio(e.audio);return;case"input_audio_buffer.commit":case"input_audio_buffer.clear":return;case"response.cancel":return;default:return}}async drain(){this.active&&await this.active}};var T=0;function G(){return T+=1,`req_${Date.now().toString(36)}_${T.toString(36)}`}var w=class{transport;constructor(e){this.transport=new p(e)}responses={create:async e=>{let t=G(),r=this.transport.sendResponses(e,t);return Object.assign((async function*(){yield*r})(),{requestId:t})}}};async function J(n){let t=await(n.fetchImpl??fetch)(`${n.catalogUrl}/model_catalog?select=model_id,variant`,{headers:{apikey:n.anonKey,Authorization:`Bearer ${n.anonKey}`}});if(!t.ok)throw new Error(`model_catalog fetch failed: ${t.status}`);return await t.json()}async function L(n){return{object:"list",data:(await J(n)).map(t=>({id:`${t.model_id}:${t.variant}`,object:"model",created:0,owned_by:"urun"}))}}var S=["session.update","conversation.item.create","response.create","response.cancel","input_audio_buffer.append","input_audio_buffer.commit","input_audio_buffer.clear"],_=["response.created","response.output_text.delta","response.output_item.added","response.function_call_arguments.delta","response.image_generation_call.partial_image","response.completed","error"];function P(n){return S.includes(n)}function O(n){return _.includes(n)}0&&(module.exports={SUPPORTED_REALTIME_CLIENT_EVENTS,SUPPORTED_RESPONSES_EVENTS,UrunRealtime,UrunResponses,isSupportedClientEvent,isSupportedResponsesEvent,listModels,nodeAudioBackend,toErrorEvent,weriftAudioBackend});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,49 +1,8 @@
|
|
|
1
|
+
import { a as UrunSessionLike, L as LaneMessage } from './ResponsesClient-CQ3l8Wft.cjs';
|
|
2
|
+
export { U as UrunResponses } from './ResponsesClient-CQ3l8Wft.cjs';
|
|
1
3
|
export { RealtimeClientEvent, RealtimeServerEvent } from 'openai/resources/beta/realtime/realtime';
|
|
2
4
|
export { ResponseStreamEvent } from 'openai/resources/responses/responses';
|
|
3
5
|
|
|
4
|
-
/** The narrow subset of @urun-sh/core's Session the SDK depends on. */
|
|
5
|
-
interface UrunDocLike {
|
|
6
|
-
set(patch: Record<string, unknown>): void;
|
|
7
|
-
get(path?: string, defaultValue?: unknown): unknown;
|
|
8
|
-
on(event: 'change', handler: (snapshot: Record<string, unknown>) => void): () => void;
|
|
9
|
-
}
|
|
10
|
-
interface UrunStreamLike {
|
|
11
|
-
messages(): AsyncIterable<unknown>;
|
|
12
|
-
attach(track: unknown): Promise<void>;
|
|
13
|
-
detach(): Promise<void>;
|
|
14
|
-
readonly track: unknown;
|
|
15
|
-
/**
|
|
16
|
-
* Subscribe to the named media track (de)appearing. The downstream audio track
|
|
17
|
-
* is consumed ASYNCHRONOUSLY after the WebRTC handshake, so `track` may be null
|
|
18
|
-
* at `enableAudio()` time; this fires with the real `MediaStreamTrack` the
|
|
19
|
-
* moment the consumer attaches (core `SessionStream.on('track')`). Optional so
|
|
20
|
-
* non-media test doubles need not implement it.
|
|
21
|
-
*/
|
|
22
|
-
on?(event: 'track', handler: (track: unknown) => void): () => void;
|
|
23
|
-
}
|
|
24
|
-
interface UrunSessionLike {
|
|
25
|
-
/** Per-leg consumer identity (core: transport.consumerId). */
|
|
26
|
-
readonly consumerId: string;
|
|
27
|
-
doc(key: string): UrunDocLike;
|
|
28
|
-
stream(name: string): UrunStreamLike;
|
|
29
|
-
close?(): Promise<void> | void;
|
|
30
|
-
}
|
|
31
|
-
/** Lane message shape emitted by the serve runtime (core session.ts:83). */
|
|
32
|
-
type LaneMessage = {
|
|
33
|
-
t: 'delta';
|
|
34
|
-
request_id?: string;
|
|
35
|
-
delta: string;
|
|
36
|
-
} | {
|
|
37
|
-
t: 'response';
|
|
38
|
-
request_id?: string;
|
|
39
|
-
body: unknown;
|
|
40
|
-
} | {
|
|
41
|
-
t: 'error';
|
|
42
|
-
request_id?: string;
|
|
43
|
-
body: unknown;
|
|
44
|
-
code?: string;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
6
|
/** Pluggable audio backend so tests don't need real WebRTC. In production, `nodeAudioBackend()` wraps @roamhq/wrtc. */
|
|
48
7
|
interface AudioSource {
|
|
49
8
|
track: unknown;
|
|
@@ -119,24 +78,6 @@ declare class UrunRealtime {
|
|
|
119
78
|
drain(): Promise<void>;
|
|
120
79
|
}
|
|
121
80
|
|
|
122
|
-
interface ResponsesStream extends AsyncIterable<unknown> {
|
|
123
|
-
readonly requestId: string;
|
|
124
|
-
}
|
|
125
|
-
declare class UrunResponses {
|
|
126
|
-
private readonly transport;
|
|
127
|
-
constructor(session: UrunSessionLike);
|
|
128
|
-
readonly responses: {
|
|
129
|
-
create: (params: {
|
|
130
|
-
model?: string;
|
|
131
|
-
input: unknown;
|
|
132
|
-
stream?: boolean;
|
|
133
|
-
tools?: unknown;
|
|
134
|
-
temperature?: number;
|
|
135
|
-
max_output_tokens?: number;
|
|
136
|
-
}) => Promise<ResponsesStream>;
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
81
|
interface ModelObject {
|
|
141
82
|
id: string;
|
|
142
83
|
object: 'model';
|
|
@@ -147,11 +88,12 @@ interface ModelList {
|
|
|
147
88
|
object: 'list';
|
|
148
89
|
data: ModelObject[];
|
|
149
90
|
}
|
|
150
|
-
|
|
91
|
+
interface CatalogOptions {
|
|
151
92
|
catalogUrl: string;
|
|
152
93
|
anonKey: string;
|
|
153
94
|
fetchImpl?: typeof fetch;
|
|
154
|
-
}
|
|
95
|
+
}
|
|
96
|
+
declare function listModels(opts: CatalogOptions): Promise<ModelList>;
|
|
155
97
|
|
|
156
98
|
interface OpenAIErrorEvent {
|
|
157
99
|
type: 'error';
|
|
@@ -171,4 +113,4 @@ declare const SUPPORTED_RESPONSES_EVENTS: readonly ["response.created", "respons
|
|
|
171
113
|
declare function isSupportedClientEvent(type: string): boolean;
|
|
172
114
|
declare function isSupportedResponsesEvent(type: string): boolean;
|
|
173
115
|
|
|
174
|
-
export { type AudioBackend, type AudioSink, type AudioSource,
|
|
116
|
+
export { type AudioBackend, type AudioSink, type AudioSource, LaneMessage, type ModelList, type ModelObject, type OpenAIErrorEvent, SUPPORTED_REALTIME_CLIENT_EVENTS, SUPPORTED_RESPONSES_EVENTS, UrunRealtime, UrunSessionLike, isSupportedClientEvent, isSupportedResponsesEvent, listModels, nodeAudioBackend, toErrorEvent, weriftAudioBackend };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,42 +1,8 @@
|
|
|
1
|
+
import { a as UrunSessionLike, L as LaneMessage } from './ResponsesClient-CQ3l8Wft.js';
|
|
2
|
+
export { U as UrunResponses } from './ResponsesClient-CQ3l8Wft.js';
|
|
1
3
|
export { RealtimeClientEvent, RealtimeServerEvent } from 'openai/resources/beta/realtime/realtime';
|
|
2
4
|
export { ResponseStreamEvent } from 'openai/resources/responses/responses';
|
|
3
5
|
|
|
4
|
-
interface UrunDocLike {
|
|
5
|
-
set(patch: Record<string, unknown>): void;
|
|
6
|
-
get(path?: string, defaultValue?: unknown): unknown;
|
|
7
|
-
on(event: 'change', handler: (snapshot: Record<string, unknown>) => void): () => void;
|
|
8
|
-
}
|
|
9
|
-
interface UrunStreamLike {
|
|
10
|
-
messages(): AsyncIterable<unknown>;
|
|
11
|
-
attach(track: unknown): Promise<void>;
|
|
12
|
-
detach(): Promise<void>;
|
|
13
|
-
readonly track: unknown;
|
|
14
|
-
|
|
15
|
-
on?(event: 'track', handler: (track: unknown) => void): () => void;
|
|
16
|
-
}
|
|
17
|
-
interface UrunSessionLike {
|
|
18
|
-
|
|
19
|
-
readonly consumerId: string;
|
|
20
|
-
doc(key: string): UrunDocLike;
|
|
21
|
-
stream(name: string): UrunStreamLike;
|
|
22
|
-
close?(): Promise<void> | void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type LaneMessage = {
|
|
26
|
-
t: 'delta';
|
|
27
|
-
request_id?: string;
|
|
28
|
-
delta: string;
|
|
29
|
-
} | {
|
|
30
|
-
t: 'response';
|
|
31
|
-
request_id?: string;
|
|
32
|
-
body: unknown;
|
|
33
|
-
} | {
|
|
34
|
-
t: 'error';
|
|
35
|
-
request_id?: string;
|
|
36
|
-
body: unknown;
|
|
37
|
-
code?: string;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
6
|
interface AudioSource {
|
|
41
7
|
track: unknown;
|
|
42
8
|
onData(frame: Int16Array): void;
|
|
@@ -91,24 +57,6 @@ declare class UrunRealtime {
|
|
|
91
57
|
drain(): Promise<void>;
|
|
92
58
|
}
|
|
93
59
|
|
|
94
|
-
interface ResponsesStream extends AsyncIterable<unknown> {
|
|
95
|
-
readonly requestId: string;
|
|
96
|
-
}
|
|
97
|
-
declare class UrunResponses {
|
|
98
|
-
private readonly transport;
|
|
99
|
-
constructor(session: UrunSessionLike);
|
|
100
|
-
readonly responses: {
|
|
101
|
-
create: (params: {
|
|
102
|
-
model?: string;
|
|
103
|
-
input: unknown;
|
|
104
|
-
stream?: boolean;
|
|
105
|
-
tools?: unknown;
|
|
106
|
-
temperature?: number;
|
|
107
|
-
max_output_tokens?: number;
|
|
108
|
-
}) => Promise<ResponsesStream>;
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
60
|
interface ModelObject {
|
|
113
61
|
id: string;
|
|
114
62
|
object: 'model';
|
|
@@ -119,11 +67,12 @@ interface ModelList {
|
|
|
119
67
|
object: 'list';
|
|
120
68
|
data: ModelObject[];
|
|
121
69
|
}
|
|
122
|
-
|
|
70
|
+
interface CatalogOptions {
|
|
123
71
|
catalogUrl: string;
|
|
124
72
|
anonKey: string;
|
|
125
73
|
fetchImpl?: typeof fetch;
|
|
126
|
-
}
|
|
74
|
+
}
|
|
75
|
+
declare function listModels(opts: CatalogOptions): Promise<ModelList>;
|
|
127
76
|
|
|
128
77
|
interface OpenAIErrorEvent {
|
|
129
78
|
type: 'error';
|
|
@@ -142,4 +91,4 @@ declare const SUPPORTED_RESPONSES_EVENTS: readonly ["response.created", "respons
|
|
|
142
91
|
declare function isSupportedClientEvent(type: string): boolean;
|
|
143
92
|
declare function isSupportedResponsesEvent(type: string): boolean;
|
|
144
93
|
|
|
145
|
-
export { type AudioBackend, type AudioSink, type AudioSource,
|
|
94
|
+
export { type AudioBackend, type AudioSink, type AudioSource, LaneMessage, type ModelList, type ModelObject, type OpenAIErrorEvent, SUPPORTED_REALTIME_CLIENT_EVENTS, SUPPORTED_RESPONSES_EVENTS, UrunRealtime, UrunSessionLike, isSupportedClientEvent, isSupportedResponsesEvent, listModels, nodeAudioBackend, toErrorEvent, weriftAudioBackend };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as f,b as
|
|
1
|
+
import{b as _}from"./chunk-OLE2YJO3.js";import{a as f,b as v,c as g}from"./chunk-FSS2A7IU.js";var p=class{constructor(t,e){this.backend=t;this.opts=e}backend;opts;source=null;sink=null;outHandlers=new Set;async startOutbound(){return this.source=this.backend.createSource(),this.source.track}appendInputAudio(t){if(!this.source)throw new Error("startOutbound() not called");let e=Buffer.from(t,"base64"),n=Math.floor(e.byteLength/2),r=new Int16Array(n);for(let a=0;a<n;a++)r[a]=e.readInt16LE(a*2);this.source.onData(r)}async startInbound(t){if(t==null)throw new Error('startInbound: no downstream audio track \u2014 the runtime audio producer has not been consumed yet (session.stream("rt-audio-out").track is null)');this.sink=this.backend.createSink(t),this.sink.onframe=e=>{let n=Buffer.from(e.samples.buffer,e.samples.byteOffset,e.samples.byteLength).toString("base64");for(let r of this.outHandlers)r(n)}}onOutputAudio(t){return this.outHandlers.add(t),()=>this.outHandlers.delete(t)}},T=24e3,c=480,x=111;async function I(){let o=await import("opusscript"),t=o.default??o;return new t(T,1,2048)}async function P(){let o=await import("werift"),t=await I();return{createSource:()=>{let e=new o.MediaStreamTrack({kind:"audio"}),n=Math.random()*4294967295>>>0,r=Math.random()*65535&65535,a=Math.random()*4294967295>>>0,i=new Int16Array(0);return{track:e,onData:u=>{let s=new Int16Array(i.length+u.length);s.set(i,0),s.set(u,i.length);let d=0;for(;s.length-d>=c;){let l=s.subarray(d,d+c);d+=c;let A=Buffer.from(l.buffer,l.byteOffset,l.byteLength),E=t.encode(A,c);r=r+1&65535,a=a+c>>>0;let b=new o.RtpHeader({version:2,payloadType:x,sequenceNumber:r,timestamp:a,ssrc:n,marker:!1}),R=new o.RtpPacket(b,E);e.writeRtp(R)}i=s.slice(d)}}},createSink:e=>{let n={onframe:null};return e.onReceiveRtp.subscribe(a=>{let i;try{i=t.decode(a.payload)}catch{return}let u=new Int16Array(i.length/2);for(let s=0;s<u.length;s++)u[s]=i.readInt16LE(s*2);n.onframe?.({samples:u})}),n}}}async function B(){let o=await import("@roamhq/wrtc");return{createSource:()=>{let t=new o.nonstandard.RTCAudioSource;return{track:t.createTrack(),onData:n=>t.onData({samples:n,sampleRate:24e3})}},createSink:t=>{let e={onframe:null},n=new o.nonstandard.RTCAudioSink(t);return n.ondata=r=>{e.onframe?.(r)},e}}}var h=0;function O(){return h+=1,`rt_${Date.now().toString(36)}_${h.toString(36)}`}var w=class{constructor(t,e={}){this.session=t;this.opts=e;this.transport=new v(t)}session;opts;transport;handlers=new Set;conversation=[];active=null;audio=null;currentRequestId=null;async enableAudio(){if(!this.opts.audioBackend)throw new Error("audioBackend required for audio");this.audio=new p(this.opts.audioBackend,{sampleRate:24e3});let t=await this.audio.startOutbound();await this.session.stream("rt-audio-in").attach(t),this.audio.onOutputAudio(n=>this.emit({type:"response.audio.delta",delta:n}));let e=await this.awaitDownstreamAudioTrack();await this.audio.startInbound(e)}awaitDownstreamAudioTrack(t=1e4){let e=this.session.stream("rt-audio-out");return e.track!=null?Promise.resolve(e.track):new Promise((n,r)=>{let a,i=setTimeout(()=>{a?.(),r(new Error(`enableAudio: no downstream audio track within ${t}ms (runtime produced no voice OUT / SFU audio consumer never attached)`))},t),u=s=>{s!=null&&(clearTimeout(i),a?.(),n(s))};a=e.on?.("track",u),e.track!=null&&u(e.track)})}on(t,e){return this.handlers.add(e),()=>this.handlers.delete(e)}emit(t){for(let e of this.handlers)e(t)}send(t){switch(t.type){case"conversation.item.create":{let e=(t.item?.content??[]).map(n=>n.text??"").join("");this.conversation.push({role:t.item?.role??"user",content:e});return}case"response.create":{let e=O();this.currentRequestId=e;let n=this.transport.sendResponseCreate({type:"response.create",response:t.response},this.conversation,e);this.active=(async()=>{try{for await(let r of n)this.emit(r)}catch(r){this.emit(f(r))}})();return}case"input_audio_buffer.append":this.audio?.appendInputAudio(t.audio);return;case"input_audio_buffer.commit":case"input_audio_buffer.clear":return;case"response.cancel":return;default:return}}async drain(){this.active&&await this.active}};var S=["session.update","conversation.item.create","response.create","response.cancel","input_audio_buffer.append","input_audio_buffer.commit","input_audio_buffer.clear"],y=["response.created","response.output_text.delta","response.output_item.added","response.function_call_arguments.delta","response.image_generation_call.partial_image","response.completed","error"];function L(o){return S.includes(o)}function C(o){return y.includes(o)}export{S as SUPPORTED_REALTIME_CLIENT_EVENTS,y as SUPPORTED_RESPONSES_EVENTS,w as UrunRealtime,g as UrunResponses,L as isSupportedClientEvent,C as isSupportedResponsesEvent,_ as listModels,B as nodeAudioBackend,f as toErrorEvent,P as weriftAudioBackend};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use strict";var se=Object.create;var E=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var oe=Object.getOwnPropertyNames;var ie=Object.getPrototypeOf,ae=Object.prototype.hasOwnProperty;var pe=(t,e)=>{for(var n in e)E(t,n,{get:e[n],enumerable:!0})},K=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of oe(e))!ae.call(t,r)&&r!==n&&E(t,r,{get:()=>e[r],enumerable:!(s=re(e,r))||s.enumerable});return t};var le=(t,e,n)=>(n=t!=null?se(ie(t)):{},K(e||!t||!t.__esModule?E(n,"default",{value:t,enumerable:!0}):n,t)),ue=t=>K(E({},"__esModule",{value:!0}),t);var Le={};pe(Le,{SessionPool:()=>U,URUN_API:()=>M,coalesceSameRole:()=>V,createUrunExtension:()=>ee,default:()=>Ie,makeStreamSimple:()=>Z,resolveSessionEnv:()=>z,toResponsesInput:()=>Q});module.exports=ue(Le);var ce=()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?document.currentScript.src:new URL("main.js",document.baseURI).href,d=ce();var X=require("@earendil-works/pi-ai");function q(t,e,n,s){let r=t.response??{},o={request_id:n,consumer_id:s,stream:!0,kind:"chat",messages:e};return typeof r.instructions=="string"&&(o.instructions=r.instructions),Array.isArray(r.modalities)&&(o.modalities=r.modalities),typeof r.temperature=="number"&&(o.temperature=r.temperature),typeof r.max_output_tokens=="number"&&(o.max_output_tokens=r.max_output_tokens),r.tools!==void 0&&(o.tools=r.tools),o}function W(t,e,n){let s={request_id:e,consumer_id:n,stream:!!t.stream,kind:"responses",input:t.input};return t.model&&(s.model=t.model),t.tools!==void 0&&(s.tools=t.tools),typeof t.temperature=="number"&&(s.temperature=t.temperature),typeof t.max_output_tokens=="number"&&(s.max_output_tokens=t.max_output_tokens),s}function G(t){if(t instanceof Error)return{type:"error",error:{type:"urun_error",code:t.name||null,message:t.message}};if(t&&typeof t=="object"&&t.t==="error"){let e=t,n=e.body??{};return{type:"error",error:{type:"urun_error",code:e.code??null,message:n.message??"unknown error"}}}return{type:"error",error:{type:"urun_error",code:null,message:String(t)}}}var de="llm-resp";async function*L(t,e){let n=`${de}:${e}`,s=`resp_${e}`;yield{type:"response.created",response:{id:s,status:"in_progress"}};for await(let r of t.stream(n).messages()){let o=r;if(o.t==="delta")yield{type:"response.output_text.delta",item_id:s,delta:o.delta};else if(o.t==="response"){yield{type:"response.completed",response:{id:s,status:"completed",...o.body}};return}else if(o.t==="error"){yield G(o);return}}}var me="llm",R=class{constructor(e){this.session=e}session;get consumerId(){return this.session.consumerId}write(e){this.session.doc(me).set({requests:{[e.request_id]:{payload:e,consumer_id:e.consumer_id,stream:e.stream}}})}sendResponses(e,n){let s=W(e,n,this.consumerId);return this.write(s),L(this.session,n)}sendResponseCreate(e,n,s){let r=q(e,n,s,this.consumerId);return this.write(r),L(this.session,s)}};var J=0;function fe(){return J+=1,`req_${Date.now().toString(36)}_${J.toString(36)}`}var x=class{transport;constructor(e){this.transport=new R(e)}responses={create:async e=>{let n=fe(),s=this.transport.sendResponses(e,n);return Object.assign((async function*(){yield*s})(),{requestId:n})}}};var $="https://api.urun.sh/v1";async function B(t){let e=t.fetchImpl??fetch,n=`${t.apiUrl.replace(/\/+$/,"")}/apps`,s=await e(n,{headers:{Authorization:`Bearer ${t.apiKey}`,Accept:"application/json"}});if(!s.ok)throw new Error(`org apps listing failed: GET ${n} \u2192 ${s.status}`);let r=await s.json();if(!Array.isArray(r.apps))throw new Error(`org apps listing returned no "apps" array (GET ${n})`);if(r.truncated===!0)throw new Error(`org apps listing was truncated (GET ${n} returned ${r.apps.length} of more) \u2014 model discovery would silently omit deployed apps`);return r.apps}async function Y(t){let e=await B(t),n=e.filter(s=>s.function_name===t.fnName&&s.deployment_status==="active").map(s=>s.app_slug);if(n.length===0)throw new Error(`urun pi extension: the org has no active deployed app exposing "${t.fnName}" (GET ${t.apiUrl}/apps returned ${e.length} app(s), none servable) \u2014 deploy one with \`urun serve <model>\` first`);return n.sort()}var M="urun-serve",ge="serve",ye=131072,he=16384,H=3,we=42e4,ve=6e4,_e=15e3;function be(t){let e=(t.URUN_API_KEY??"").trim();if(!e)throw new Error("urun pi extension: URUN_API_KEY is required \u2014 model discovery lists the org's deployed serve apps via the org API (URUN_JWT alone cannot list apps)");return{apiKey:e,apiUrl:(t.URUN_API_URL??"").trim()||$,fnName:(t.URUN_FUNCTION??"").trim()||ge}}function z(t){let e=(t.URUN_BASE_URL??"").trim(),n=(t.URUN_ORG_ID??"").trim();if(!e)throw new Error("urun pi extension: URUN_BASE_URL is required to open a session");if(/\/v1\/*$/.test(new URL(e).pathname))throw new Error(`urun pi extension: URUN_BASE_URL must be the session-gateway base (e.g. https://api.urun.sh), got ${e} \u2014 a trailing /v1 is the org API form (URUN_API_URL); session allocation 404s against it. Drop the /v1.`);if(!n)throw new Error("urun pi extension: URUN_ORG_ID is required to open a session");let s=(t.URUN_JWT??"").trim();if(s)return{baseUrl:e,orgId:n,auth:{lane:"jwt",jwt:s}};let r=(t.URUN_API_KEY??"").trim();if(!r)throw new Error("urun pi extension: URUN_JWT or URUN_API_KEY is required to open a session");let o=(t.URUN_GATEWAY_URL??"").trim()||void 0;return{baseUrl:e,orgId:n,auth:{lane:"api-key",apiKey:r,gatewayUrl:o}}}var Ae=async(t,e,n)=>{let{baseUrl:s,orgId:r,auth:o}=z(t),{createRequire:a}=await import("module"),l=a(d);if(typeof globalThis.RTCPeerConnection>"u")try{l.resolve("werift")}catch{throw new Error('urun pi extension: token streams require a WebRTC data channel, and this Node runtime has no RTCPeerConnection and no "werift" backend installed (@urun-sh/core\'s optional Node WebRTC backend \u2014 it was probably stripped by a --no-optional install). Install werift (`npm i werift`) and retry.')}let{App:m,createClientToken:p}=l("@urun-sh/core"),y=(o.lane==="jwt"?m(e,{baseUrl:s,orgId:r,jwt:o.jwt}):m(e,{baseUrl:s,orgId:r,getAccessToken:async()=>(await p(o.apiKey,{baseUrl:o.gatewayUrl,expiresIn:300,allowedFunctions:[`${e}/${n}`]})).token}))[n];if(typeof y!="function")throw new Error(`urun pi extension: app "${e}" has no function "${n}"`);let u=y(),c=u.connect;return typeof c=="function"&&await c.call(u),u};function Ee(t){try{Promise.resolve(t.close?.()).catch(()=>{})}catch{}}var U=class{constructor(e,n,s){this.open=e;this.env=n;this.fnName=s}open;env;fnName;pool=new Map;acquire(e){let n=this.pool.get(e);if(!n){let s=Promise.resolve(this.open(this.env,e,this.fnName)).then(r=>({session:r,responses:new x(r)}));n=s,this.pool.set(e,s),s.catch(()=>{this.pool.get(e)===s&&this.pool.delete(e)})}return n}evict(e){let n=this.pool.get(e);n&&(this.pool.delete(e),n.then(s=>Ee(s.session),()=>{}))}closeAll(){for(let e of[...this.pool.keys()])this.evict(e)}};function Q(t){let e=[];t.systemPrompt&&e.push({role:"system",content:t.systemPrompt});for(let n of t.messages){let s=n.role==="assistant"?"assistant":"user";e.push({role:s,content:Re(n.content)})}return V(e)}function V(t){let e=[];for(let n of t){let s=e[e.length-1];s&&s.role===n.role?s.content=[s.content,n.content].filter(r=>r.length>0).join(`
|
|
2
|
+
|
|
3
|
+
`):e.push({...n})}return e}function Re(t){return typeof t=="string"?t:Array.isArray(t)?t.map(e=>e&&typeof e=="object"&&"text"in e?String(e.text):"").join(""):""}function xe(t){let e=t instanceof Error?t.message:String(t),n=t?.code;return/\b529\b/.test(e)||/overloaded/i.test(e)||n===529||n==="529"}function Ue(t){return new Promise(e=>setTimeout(e,t))}function Se(t,e,n,s){return new Promise((r,o)=>{let a=!1,l=i=>{a||(a=!0,clearTimeout(m),s?.removeEventListener("abort",p),i())},m=setTimeout(()=>l(()=>o(n())),e),p=()=>l(()=>o(new Error("aborted")));if(s?.aborted){p();return}s?.addEventListener("abort",p,{once:!0}),Promise.resolve(t).then(i=>l(()=>r(i)),i=>l(()=>o(i)))})}async function*ke(t,e){let n=t[Symbol.asyncIterator](),s,r=new Promise((o,a)=>{s=()=>a(new Error("aborted")),e.addEventListener("abort",s,{once:!0})});try{for(;;){if(e.aborted)throw new Error("aborted");let o=await Promise.race([n.next(),r]);if(o.done)return;yield o.value}}finally{s&&e.removeEventListener("abort",s),n.return?.(void 0)}}function Te(t,e){let n=t?.sendMessage;if(typeof n=="function")try{n.call(t,{customType:"urun-status",content:e,display:!0},{triggerTurn:!1})}catch{}}function Z(t,e={},n){let s=e.connectDeadlineMs??we,r=e.stallTimeoutMs??ve,o=e.phaseHeartbeatMs??_e;return(a,l,m)=>{let p=(0,X.createAssistantMessageEventStream)(),i={role:"assistant",content:[{type:"text",text:""}],api:a.api,provider:a.provider,model:a.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:Date.now()},y=i.content[0],u=new AbortController,c=m?.signal,S=!1,k=()=>{S=!0,u.abort()};c&&(c.aborted?k():c.addEventListener("abort",k,{once:!0}));let T=!1,h,C=()=>{h&&clearTimeout(h),h=setTimeout(()=>{T=!0,u.abort()},r)},P=()=>{h&&clearTimeout(h),h=void 0};return(async()=>{p.push({type:"start",partial:i}),p.push({type:"text_start",contentIndex:0,partial:i});let N=!1,_,D=()=>{_&&clearInterval(_),_=void 0},te=Date.now();_=setInterval(()=>{let f=Math.round((Date.now()-te)/1e3);Te(n,`uRun: still opening the "${a.id}" session\u2026 (${f}s \u2014 serve apps scale to zero, first turn can cold-start ~6min)`)},o);try{let{responses:f}=await Se(t.acquire(a.id),s,()=>new Error(`uRun session for "${a.id}" did not connect within ${Math.round(s/1e3)}s \u2014 likely cold-starting (scaled to zero) or queued behind capacity. Retry shortly.`),u.signal);D();let I=Q(l),O,b;for(let A=1;A<=H;A++)try{let w=await f.responses.create({model:a.id,input:I,stream:!0,max_output_tokens:a.maxTokens});C();for await(let ne of ke(w,u.signal)){C();let g=ne;if(g.type==="response.output_text.delta"){let v=typeof g.delta=="string"?g.delta:"";v&&(y.text+=v,p.push({type:"text_delta",contentIndex:0,delta:v,partial:i}))}else if(g.type==="response.completed"){O=g.response;break}else if(g.type==="error"){let v=g.error,F=new Error(`uRun serve error: ${v?.message??"unknown error"}`);throw F.code=v?.code??null,F}}b=void 0;break}catch(w){if(b=w,u.signal.aborted)throw w;if(A<H&&xe(w)&&y.text.length===0){await Ue(A*500);continue}throw w}if(b)throw b;P(),p.push({type:"text_end",contentIndex:0,content:y.text,partial:i});let j=O?.status==="incomplete"?"length":"stop";i.stopReason=j,p.push({type:"done",reason:j,message:i}),p.end(i)}catch(f){if(N=!0,P(),S||c?.aborted&&!T)i.stopReason="aborted",i.errorMessage="aborted by user",p.push({type:"error",reason:"aborted",error:i});else{let I=T?`uRun stream stalled \u2014 no output for ${Math.round(r/1e3)}s; the model may be hung. Retry, or Ctrl-C to abort.`:f instanceof Error?f.message:String(f);i.stopReason="error",i.errorMessage=I,p.push({type:"error",reason:"error",error:i})}p.end(i)}finally{P(),D(),c&&c.removeEventListener("abort",k),N&&t.evict(a.id)}})(),p}}function Pe(t){return{id:t,name:`uRun ${t}`,api:M,reasoning:!1,input:["text"],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:ye,maxTokens:he}}function ee(t={}){return async e=>{let n=t.env??process.env,{apiKey:s,apiUrl:r,fnName:o}=be(n),a=await Y({apiUrl:r,apiKey:s,fnName:o,fetchImpl:t.fetchImpl}),l=new U(t.openSession??Ae,n,o);e.on("session_shutdown",()=>l.closeAll());let m={name:"uRun",api:M,baseUrl:"urun://session",apiKey:"$URUN_API_KEY",models:a.map(Pe),streamSimple:Z(l,t.timing??{},e)};e.registerProvider("urun",m)}}var Ie=ee();0&&(module.exports={SessionPool,URUN_API,coalesceSameRole,createUrunExtension,makeStreamSimple,resolveSessionEnv,toResponsesInput});
|