Skip to content

Instantly share code, notes, and snippets.

@maccman
Created June 1, 2025 22:49
Show Gist options
  • Save maccman/4a11a6b6b968dd6937cb0ebbea5ba8c9 to your computer and use it in GitHub Desktop.
Save maccman/4a11a6b6b968dd6937cb0ebbea5ba8c9 to your computer and use it in GitHub Desktop.
import type { ZodType } from 'zod'
import { z } from 'zod'
const API_BASE_URL = 'https://api.pipecat.daily.co/v1'
// Schemas
const AutoScalingConfigSchema = z.object({
minAgents: z.number().optional(),
maxAgents: z.number().optional(),
})
export const CreateAgentRequestSchema = z.object({
serviceName: z.string().regex(/^[\da-z]([\da-z-]*[\da-z])$/),
image: z.string(),
nodeType: z.literal('arm').optional().default('arm'),
imagePullSecretSet: z.string().optional(),
secretSet: z.string().optional(),
autoScaling: AutoScalingConfigSchema.optional(),
enableKrisp: z.boolean().optional().default(false),
})
const AgentAutoScalingInfoSchema = z.object({
maxReplicas: z.number().optional(),
minReplicas: z.number().optional(),
})
const KrispModelsSchema = z.object({
enabled: z.boolean().optional(),
})
const SpecAutoScalingSchema = z.object({
minReplicas: z.number().optional(),
maxReplicas: z.number().optional(),
})
const ManifestMetadataSchema = z.object({
name: z.string().optional(),
namespace: z.string().optional(),
})
const ManifestSpecSchema = z.object({
dailyNodeType: z.string().optional(),
clusterLocal: z.boolean().optional(),
image: z.string().optional(),
autoScaling: SpecAutoScalingSchema.optional(),
envFromSecretNames: z.array(z.string()).optional(),
krispModels: KrispModelsSchema.optional(),
})
const DeploymentManifestSchema = z.object({
apiVersion: z.string().optional(),
kind: z.string().optional(),
metadata: ManifestMetadataSchema.optional(),
spec: ManifestSpecSchema.optional(),
})
const AgentDeploymentSchema = z.object({
id: z.string().optional(),
manifest: DeploymentManifestSchema.optional(),
serviceId: z.string().optional(),
createdAt: z.string().datetime().optional(),
updatedAt: z.string().datetime().optional(),
})
export const AgentDetailsResponseSchema = z.object({
name: z.string().optional(),
ready: z.boolean().optional(),
createdAt: z.string().datetime().optional(),
updatedAt: z.string().datetime().optional(),
activeDeploymentId: z.string().optional(),
activeDeploymentReady: z.boolean().optional(),
autoScaling: AgentAutoScalingInfoSchema.optional(),
activeSessionCount: z.number().optional(),
deployment: AgentDeploymentSchema.nullable().optional(),
})
export const ErrorResponseSchema = z.object({
error: z.string().optional(),
code: z.string().optional(),
})
export const DeleteAgentResponseSchema = z.object({
status: z.string(),
})
export const UpdateAgentRequestSchema = z.object({
image: z.string().optional(),
nodeType: z.literal('arm').optional(),
imagePullSecretSet: z.string().optional(),
secretSet: z.string().optional(),
autoScaling: AutoScalingConfigSchema.optional(),
enableKrisp: z.boolean().optional(),
})
// Schema for Start an Agent session request
export const StartSessionRequestSchema = z.object({
createDailyRoom: z.boolean(),
dailyRoomProperties: z.record(z.string(), z.unknown()).optional(),
body: z.record(z.string(), z.unknown()).optional(),
})
// Schema for Start an Agent session response
export const StartSessionResponseSchema = z.object({
dailyRoom: z.string().url().optional(),
dailyToken: z.string().optional(),
})
// Inferred Types
export type CreateAgentRequest = z.infer<typeof CreateAgentRequestSchema>
export type UpdateAgentRequest = z.infer<typeof UpdateAgentRequestSchema>
export type StartSessionRequest = z.infer<typeof StartSessionRequestSchema>
export type AgentDetailsResponse = z.infer<typeof AgentDetailsResponseSchema>
export type StartSessionResponse = z.infer<typeof StartSessionResponseSchema>
export type ErrorResponse = z.infer<typeof ErrorResponseSchema>
export type DeleteAgentResponse = z.infer<typeof DeleteAgentResponseSchema>
// Custom Error for API non-ok responses
export class PipecatApiError extends Error {
constructor(
message: string,
public statusCode: number,
public details?: ErrorResponse,
) {
super(message)
this.name = 'PipecatApiError'
}
}
export class PipecatApiClient {
private readonly apiToken: string
private readonly baseUrl: string
constructor(apiToken: string, baseUrl: string = API_BASE_URL) {
this.apiToken = apiToken
this.baseUrl = baseUrl
}
private async request<TRequestBody, TResponseBody>(
path: string,
method: 'POST' | 'GET' | 'PUT' | 'DELETE',
requestBody: TRequestBody,
requestSchema: ZodType<TRequestBody>,
responseSchema: ZodType<TResponseBody>,
): Promise<TResponseBody> {
const validatedRequestBody = requestSchema.parse(requestBody)
const response = await fetch(`${this.baseUrl}${path}`, {
method,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.apiToken}`,
},
body: JSON.stringify(validatedRequestBody),
})
let responseBody
try {
responseBody = (await response.json()) as unknown
} catch (_err) {
throw new PipecatApiError(
'Failed to parse API response',
response.status,
{ error: 'Invalid JSON response' },
)
}
if (!response.ok) {
const parsedError = ErrorResponseSchema.parse(responseBody)
throw new PipecatApiError(
parsedError.error || `API Error: ${response.status}`,
response.status,
parsedError,
)
}
return responseSchema.parse(responseBody)
}
async createAgent(
requestBody: CreateAgentRequest,
): Promise<AgentDetailsResponse> {
return await this.request(
'/agents',
'POST',
requestBody,
CreateAgentRequestSchema,
AgentDetailsResponseSchema,
)
}
async updateAgent(
agentName: string,
requestBody: UpdateAgentRequest,
): Promise<AgentDetailsResponse> {
return await this.request(
`/agents/${agentName}`,
'POST',
requestBody,
UpdateAgentRequestSchema,
AgentDetailsResponseSchema,
)
}
async startSession(
agentName: string,
requestBody: StartSessionRequest,
): Promise<StartSessionResponse> {
return await this.request(
`/${agentName}/start`,
'POST',
requestBody,
StartSessionRequestSchema,
StartSessionResponseSchema,
)
}
async getAgentDetails(agentName: string): Promise<AgentDetailsResponse> {
return await this.request(
`/agents/${agentName}`,
'GET',
{}, // No request body for GET
z.object({}), // Schema for an empty object
AgentDetailsResponseSchema,
)
}
async deleteAgent(agentName: string): Promise<DeleteAgentResponse> {
return await this.request(
`/agents/${agentName}`,
'DELETE',
{},
z.object({}), // No request body for DELETE
DeleteAgentResponseSchema,
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment