Skip to content
← Public packages

@cameronpak/zo-computer

Typed Zo Computer API client with Result-based error handling and SSE streaming.

src/list-models.ts

59 lines · 1.8 KB · TypeScript
import { Result } from 'better-result'
import { isRecord, zoJson } from './core'
import { ZoParseError, type ZoRequestError } from './errors'
import type { ZoClientOptions, ZoModel } from './types'

function toModel(raw: unknown): ZoModel | null {
	if (!isRecord(raw)) return null
	if (typeof raw.model_name !== 'string') return null
	return {
		modelName: raw.model_name,
		label: typeof raw.label === 'string' ? raw.label : raw.model_name,
		vendor: typeof raw.vendor === 'string' ? raw.vendor : '',
		description: typeof raw.description === 'string' ? raw.description : null,
		type: typeof raw.type === 'string' ? raw.type : null,
		contextWindow:
			typeof raw.context_window === 'number' ? raw.context_window : null,
		isByok: raw.is_byok === true,
	}
}

/**
 * List the models you can pass to `ask` as `modelName`.
 *
 * This is a read-only call. Use it as a smoke test for a new API key.
 *
 * @example
 * import listModels from 'kody:@cameronpak/zo-computer/list-models'
 * const models = await listModels({})
 * if (models.status === 'ok') console.log(models.value.length)
 */
export default async function listModels(
	params: ZoClientOptions = {},
): Promise<Result<Array<ZoModel>, ZoRequestError>> {
	const parsed = await zoJson(
		'/models/available',
		{ method: 'GET', accept: 'application/json' },
		params,
	)
	if (Result.isError(parsed)) return parsed

	const payload = parsed.value
	if (!isRecord(payload) || !Array.isArray(payload.models)) {
		return Result.err(
			new ZoParseError({
				url: '/models/available',
				body: JSON.stringify(payload).slice(0, 2000),
				message: 'Zo response did not contain a models array',
			}),
		)
	}

	const models: Array<ZoModel> = []
	for (const entry of payload.models) {
		const model = toModel(entry)
		if (model !== null) models.push(model)
	}

	return Result.ok(models)
}