UNPKG

phonic

Version:
604 lines (593 loc) 16.3 kB
// package.json var version = "0.29.0"; // src/agents/index.ts var Agents = class { constructor(phonic) { this.phonic = phonic; } getQueryString(params) { const project = params?.project; const queryString = new URLSearchParams({ ...project !== void 0 && { project } }).toString(); return queryString; } getTemplateVariablesForBody(templateVariables) { if (templateVariables === void 0) { return void 0; } return Object.fromEntries( Object.entries(templateVariables).map(([key, value]) => [ key, { default_value: value.defaultValue } ]) ); } getConfigurationEndpointForBody(configurationEndpoint) { if (configurationEndpoint === void 0 || configurationEndpoint === null) { return configurationEndpoint; } return { url: configurationEndpoint.url, headers: configurationEndpoint.headers, timeout_ms: configurationEndpoint.timeoutMs }; } async list(params) { const response = await this.phonic.get( `/agents?${this.getQueryString(params)}` ); return response; } async get(nameOrId, params) { const response = await this.phonic.get( `/agents/${nameOrId}?${this.getQueryString(params)}` ); return response; } async create(params) { const response = await this.phonic.post( `/agents?${this.getQueryString(params)}`, { name: params.name, phone_number: params.phoneNumber, timezone: params.timezone, audio_format: params.phoneNumber === "assign-automatically" ? "mulaw_8000" : params.audioFormat, voice_id: params.voiceId, welcome_message: params.welcomeMessage, system_prompt: params.systemPrompt, template_variables: this.getTemplateVariablesForBody( params.templateVariables ), tools: params.tools, no_input_poke_sec: params.noInputPokeSec, no_input_poke_text: params.noInputPokeText, no_input_end_conversation_sec: params.noInputEndConversationSec, boosted_keywords: params.boostedKeywords, configuration_endpoint: this.getConfigurationEndpointForBody( params.configurationEndpoint ) } ); return response; } async update(nameOrId, params) { const response = await this.phonic.patch( `/agents/${nameOrId}?${this.getQueryString(params)}`, { name: params.name, phone_number: params.phoneNumber, timezone: params.timezone, audio_format: params.phoneNumber === "assign-automatically" ? "mulaw_8000" : params.audioFormat, voice_id: params.voiceId, welcome_message: params.welcomeMessage, system_prompt: params.systemPrompt, template_variables: this.getTemplateVariablesForBody( params.templateVariables ), tools: params.tools, no_input_poke_sec: params.noInputPokeSec, no_input_poke_text: params.noInputPokeText, no_input_end_conversation_sec: params.noInputEndConversationSec, boosted_keywords: params.boostedKeywords, configuration_endpoint: this.getConfigurationEndpointForBody( params.configurationEndpoint ) } ); return response; } async upsert(params) { const response = await this.phonic.put( `/agents/upsert?${this.getQueryString(params)}`, { name: params.name, phone_number: params.phoneNumber, timezone: params.timezone, audio_format: params.phoneNumber === "assign-automatically" ? "mulaw_8000" : params.audioFormat, voice_id: params.voiceId, welcome_message: params.welcomeMessage, system_prompt: params.systemPrompt, template_variables: this.getTemplateVariablesForBody( params.templateVariables ), tools: params.tools, no_input_poke_sec: params.noInputPokeSec, no_input_poke_text: params.noInputPokeText, no_input_end_conversation_sec: params.noInputEndConversationSec, boosted_keywords: params.boostedKeywords, configuration_endpoint: this.getConfigurationEndpointForBody( params.configurationEndpoint ) } ); return response; } async delete(nameOrId, params) { const response = await this.phonic.delete( `/agents/${nameOrId}?${this.getQueryString(params)}` ); return response; } }; // src/conversations/twilio/index.ts var Twilio = class { constructor(phonic) { this.phonic = phonic; } async outboundCall(params, config) { const response = await this.phonic.post( "/conversations/twilio/outbound_call", { from_phone_number: params.from_phone_number, to_phone_number: params.to_phone_number, config }, { "X-Twilio-Account-Sid": params.account_sid, "X-Twilio-Api-Key-Sid": params.api_key_sid, "X-Twilio-Api-Key-Secret": params.api_key_secret } ); return response; } }; // src/conversations/index.ts var Conversations = class { constructor(phonic) { this.phonic = phonic; this.twilio = new Twilio(phonic); } twilio; async list({ project, durationMin, durationMax, startedAtMin, startedAtMax }) { const queryString = new URLSearchParams({ ...project !== void 0 && { project }, ...durationMin !== void 0 && { duration_min: String(durationMin) }, ...durationMax !== void 0 && { duration_max: String(durationMax) }, ...startedAtMin !== void 0 && { started_at_min: startedAtMin }, ...startedAtMax !== void 0 && { started_at_max: startedAtMax } }).toString(); const response = await this.phonic.get( `/conversations?${queryString}` ); return response; } async get(id) { const response = await this.phonic.get( `/conversations/${id}` ); return response; } async getByExternalId({ project, externalId }) { const queryString = new URLSearchParams({ ...project !== void 0 && { project }, external_id: externalId }).toString(); const response = await this.phonic.get( `/conversations?${queryString}` ); return response; } async outboundCall(toPhoneNumber, config) { const response = await this.phonic.post( "/conversations/outbound_call", { to_phone_number: toPhoneNumber, config } ); return response; } }; // src/projects/index.ts var Projects = class { constructor(phonic) { this.phonic = phonic; } async list() { const response = await this.phonic.get("/projects"); return response; } async get(nameOrId) { const response = await this.phonic.get( `/projects/${nameOrId}` ); return response; } async create(params) { const response = await this.phonic.post( "/projects", { name: params.name } ); return response; } async update(nameOrId, params) { const response = await this.phonic.patch( `/projects/${nameOrId}`, { name: params.name, default_agent: params.defaultAgent } ); return response; } async delete(nameOrId) { const response = await this.phonic.delete( `/projects/${nameOrId}` ); return response; } }; // src/sts/index.ts import WebSocket from "ws"; // src/sts/websocket.ts var PhonicSTSWebSocket = class { constructor(ws, config) { this.ws = ws; this.config = config; this.buffer.push( JSON.stringify({ type: "config", ...this.config }) ); this.ws.onopen = () => { for (const message of this.buffer) { this.ws.send(message); } this.isOpen = true; }; this.ws.onmessage = (event) => { if (this.onMessageCallback === null) { return; } if (typeof event.data !== "string") { throw new Error("Received non-string message"); } const dataObj = JSON.parse( event.data ); this.onMessageCallback(dataObj); }; this.ws.onclose = (event) => { if (this.onCloseCallback === null) { return; } this.onCloseCallback(event); }; this.ws.onerror = (event) => { if (this.onErrorCallback === null) { return; } this.onErrorCallback(event); }; this.onMessage = this.onMessage.bind(this); this.onClose = this.onClose.bind(this); this.onError = this.onError.bind(this); this.audioChunk = this.audioChunk.bind(this); this.sendToolCallOutput = this.sendToolCallOutput.bind(this); this.updateSystemPrompt = this.updateSystemPrompt.bind(this); this.setExternalId = this.setExternalId.bind(this); this.close = this.close.bind(this); } onMessageCallback = null; onCloseCallback = null; onErrorCallback = null; buffer = []; isOpen = false; processUserMessage(message) { const messageStr = JSON.stringify(message); if (this.isOpen) { this.ws.send(messageStr); } else { this.buffer.push(messageStr); } } onMessage(callback) { this.onMessageCallback = callback; } onClose(callback) { this.onCloseCallback = callback; } onError(callback) { this.onErrorCallback = callback; } audioChunk({ audio }) { this.processUserMessage({ type: "audio_chunk", audio }); } sendToolCallOutput({ toolCallId, output }) { this.processUserMessage({ type: "tool_call_output", tool_call_id: toolCallId, output }); } updateSystemPrompt({ systemPrompt }) { this.processUserMessage({ type: "update_system_prompt", system_prompt: systemPrompt }); } setExternalId({ externalId }) { this.processUserMessage({ type: "set_external_id", external_id: externalId }); } close(code) { this.ws.close(code ?? 1e3); } }; // src/sts/index.ts var SpeechToSpeech = class { constructor(phonic) { this.phonic = phonic; } websocket(config) { const wsBaseUrl = this.phonic.baseUrl.replace(/^http/, "ws"); const queryString = new URLSearchParams({ ...this.phonic.__downstreamWebSocketUrl !== null && { downstream_websocket_url: this.phonic.__downstreamWebSocketUrl } }).toString(); const phonicApiWsUrl = `${wsBaseUrl}/v1/sts/ws?${queryString}`; const ws = new WebSocket(phonicApiWsUrl, { headers: this.phonic.headers }); return new PhonicSTSWebSocket(ws, config); } }; // src/tools/index.ts var Tools = class { constructor(phonic) { this.phonic = phonic; } getQueryString(params) { const project = params?.project; const queryString = new URLSearchParams({ ...project !== void 0 && { project } }).toString(); return queryString; } getParametersForBody(parameters) { if (parameters === void 0) { return void 0; } return parameters.map((parameter) => { return { type: parameter.type, name: parameter.name, description: parameter.description, is_required: parameter.isRequired, ...parameter.type === "array" && { item_type: parameter.itemType } }; }); } async list(params) { const response = await this.phonic.get( `/tools?${this.getQueryString(params)}` ); return response; } async get(nameOrId, params) { const response = await this.phonic.get( `/tools/${nameOrId}?${this.getQueryString(params)}` ); return response; } async create(params) { const body = { name: params.name, description: params.description, type: params.type, execution_mode: params.executionMode, parameters: this.getParametersForBody(params.parameters) }; if (params.type === "custom_webhook") { body.endpoint_method = params.endpointMethod; body.endpoint_url = params.endpointUrl; body.endpoint_headers = params.endpointHeaders; body.endpoint_timeout_ms = params.endpointTimeoutMs; } if (params.type === "custom_websocket") { body.tool_call_output_timeout_ms = params.toolCallOutputTimeoutMs; } const response = await this.phonic.post( `/tools?${this.getQueryString(params)}`, body ); return response; } async update(nameOrId, params) { const response = await this.phonic.patch( `/tools/${nameOrId}?${this.getQueryString(params)}`, { name: params.name, description: params.description, type: params.type, execution_mode: params.executionMode, endpoint_method: params.endpointMethod, endpoint_url: params.endpointUrl, endpoint_headers: params.endpointHeaders, endpoint_timeout_ms: params.endpointTimeoutMs, parameters: this.getParametersForBody(params.parameters), tool_call_output_timeout_ms: params.toolCallOutputTimeoutMs } ); return response; } async delete(nameOrId, params) { const response = await this.phonic.delete( `/tools/${nameOrId}?${this.getQueryString(params)}` ); return response; } }; // src/voices/index.ts var Voices = class { constructor(phonic) { this.phonic = phonic; } async list({ model }) { const response = await this.phonic.get( `/voices?model=${encodeURIComponent(model)}` ); return response; } async get(id) { const response = await this.phonic.get( `/voices/${id}` ); return response; } }; // src/phonic.ts var defaultBaseUrl = "https://api.phonic.co"; var defaultUserAgent = `phonic-node:${version}`; var Phonic = class { constructor(apiKey, config) { this.apiKey = apiKey; if (typeof process === "undefined") { throw new Error( "Phonic SDK is intended to be used in Node.js environment." ); } if (!this.apiKey) { throw new Error( 'API key is missing. Pass it to the constructor: `new Phonic("ph_...")`' ); } this.baseUrl = (config?.baseUrl ?? defaultBaseUrl).replace(/\/$/, ""); this.__downstreamWebSocketUrl = config?.__downstreamWebSocketUrl || null; this.headers = { Authorization: `Bearer ${this.apiKey}`, "User-Agent": process.env.PHONIC_USER_AGENT || defaultUserAgent, "Content-Type": "application/json", ...config?.headers }; } baseUrl; __downstreamWebSocketUrl; headers; agents = new Agents(this); conversations = new Conversations(this); projects = new Projects(this); tools = new Tools(this); voices = new Voices(this); sts = new SpeechToSpeech(this); async fetchRequest(path, options) { try { const { headers, ...restOptions } = options; const response = await fetch(`${this.baseUrl}/v1${path}`, { headers: { ...this.headers, ...headers }, ...restOptions }); if (response.ok) { const data = await response.json(); return { data, error: null }; } try { const data = await response.json(); return { data: null, error: { message: data.error.message || response.statusText, param_errors: data.param_errors } }; } catch (error) { return { data: null, error: { message: response.statusText } }; } } catch (error) { console.error(error); return { data: null, error: { message: "Fetch request failed" } }; } } async get(path) { return this.fetchRequest(path, { method: "GET" }); } async post(path, body, headers) { return this.fetchRequest(path, { method: "POST", body: JSON.stringify(body), headers }); } async patch(path, body, headers) { return this.fetchRequest(path, { method: "PATCH", body: JSON.stringify(body), headers }); } async put(path, body, headers) { return this.fetchRequest(path, { method: "PUT", body: JSON.stringify(body), headers }); } async delete(path, headers) { return this.fetchRequest(path, { method: "DELETE", headers }); } }; export { Phonic };