668 lines
20 KiB
TypeScript
668 lines
20 KiB
TypeScript
import type {
|
|
AuthResponse,
|
|
BaseModelCatalogItem,
|
|
BaseModelUpsertRequest,
|
|
CatalogProvider,
|
|
CatalogProviderUpsertRequest,
|
|
CreatedGatewayApiKey,
|
|
GatewayAccessRuleBatchRequest,
|
|
GatewayAccessRule,
|
|
GatewayAccessRuleUpsertRequest,
|
|
GatewayApiKey,
|
|
GatewayTenant,
|
|
GatewayTenantUpsertRequest,
|
|
GatewayTask,
|
|
GatewayUser,
|
|
GatewayUserUpsertRequest,
|
|
IntegrationPlatform,
|
|
ListResponse,
|
|
ModelCatalogResponse,
|
|
PlatformModel,
|
|
PlayableGatewayApiKey,
|
|
PricingRule,
|
|
PricingRuleSet,
|
|
PricingRuleSetUpsertRequest,
|
|
RateLimitWindow,
|
|
RuntimePolicySet,
|
|
RuntimePolicySetUpsertRequest,
|
|
UserGroup,
|
|
UserGroupUpsertRequest,
|
|
} from '@easyai-ai-gateway/contracts';
|
|
import type { PlatformCreateInput, PlatformModelBindingInput, WorkspaceTaskQuery } from './types';
|
|
|
|
const API_BASE = import.meta.env.VITE_GATEWAY_API_BASE_URL ?? 'http://localhost:8088';
|
|
|
|
export interface HealthResponse {
|
|
ok: boolean;
|
|
service: string;
|
|
env: string;
|
|
identityMode?: string;
|
|
}
|
|
|
|
export async function getHealth(): Promise<HealthResponse> {
|
|
return request<HealthResponse>('/healthz', { auth: false });
|
|
}
|
|
|
|
export async function registerLocalAccount(input: {
|
|
username: string;
|
|
email?: string;
|
|
password: string;
|
|
displayName?: string;
|
|
invitationCode?: string;
|
|
}): Promise<AuthResponse> {
|
|
return request<AuthResponse>('/api/v1/auth/register', {
|
|
auth: false,
|
|
body: input,
|
|
method: 'POST',
|
|
});
|
|
}
|
|
|
|
export async function loginLocalAccount(input: { account: string; password: string }): Promise<AuthResponse> {
|
|
return request<AuthResponse>('/api/v1/auth/login', {
|
|
auth: false,
|
|
body: input,
|
|
method: 'POST',
|
|
});
|
|
}
|
|
|
|
export async function listPlatforms(token: string): Promise<ListResponse<IntegrationPlatform>> {
|
|
return request<ListResponse<IntegrationPlatform>>('/api/admin/platforms', { token });
|
|
}
|
|
|
|
export async function listModels(token: string): Promise<ListResponse<PlatformModel>> {
|
|
return request<ListResponse<PlatformModel>>('/api/admin/models', { token });
|
|
}
|
|
|
|
export async function listPlayableModels(token: string): Promise<ListResponse<PlatformModel>> {
|
|
return request<ListResponse<PlatformModel>>('/api/v1/models', { token });
|
|
}
|
|
|
|
export async function listModelCatalog(token: string): Promise<ModelCatalogResponse> {
|
|
return request<ModelCatalogResponse>('/api/v1/model-catalog', { token });
|
|
}
|
|
|
|
export async function listPublicCatalogProviders(): Promise<ListResponse<CatalogProvider>> {
|
|
return request<ListResponse<CatalogProvider>>('/api/v1/public/catalog/providers', { auth: false });
|
|
}
|
|
|
|
export async function listCatalogProviders(token: string): Promise<ListResponse<CatalogProvider>> {
|
|
return request<ListResponse<CatalogProvider>>('/api/admin/catalog/providers', { token });
|
|
}
|
|
|
|
export async function createCatalogProvider(
|
|
token: string,
|
|
input: CatalogProviderUpsertRequest,
|
|
): Promise<CatalogProvider> {
|
|
return request<CatalogProvider>('/api/admin/catalog/providers', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function updateCatalogProvider(
|
|
token: string,
|
|
providerId: string,
|
|
input: CatalogProviderUpsertRequest,
|
|
): Promise<CatalogProvider> {
|
|
return request<CatalogProvider>(`/api/admin/catalog/providers/${providerId}`, {
|
|
body: input,
|
|
method: 'PATCH',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deleteCatalogProvider(token: string, providerId: string): Promise<void> {
|
|
await request<void>(`/api/admin/catalog/providers/${providerId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function listPublicBaseModels(): Promise<ListResponse<BaseModelCatalogItem>> {
|
|
return request<ListResponse<BaseModelCatalogItem>>('/api/v1/public/catalog/base-models', { auth: false });
|
|
}
|
|
|
|
export async function listBaseModels(token: string): Promise<ListResponse<BaseModelCatalogItem>> {
|
|
return request<ListResponse<BaseModelCatalogItem>>('/api/admin/catalog/base-models', { token });
|
|
}
|
|
|
|
export async function createBaseModel(token: string, input: BaseModelUpsertRequest): Promise<BaseModelCatalogItem> {
|
|
return request<BaseModelCatalogItem>('/api/admin/catalog/base-models', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function updateBaseModel(
|
|
token: string,
|
|
baseModelId: string,
|
|
input: BaseModelUpsertRequest,
|
|
): Promise<BaseModelCatalogItem> {
|
|
return request<BaseModelCatalogItem>(`/api/admin/catalog/base-models/${baseModelId}`, {
|
|
body: input,
|
|
method: 'PATCH',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function resetBaseModel(token: string, baseModelId: string): Promise<BaseModelCatalogItem> {
|
|
return request<BaseModelCatalogItem>(`/api/admin/catalog/base-models/${baseModelId}/reset`, {
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function resetAllBaseModels(token: string): Promise<ListResponse<BaseModelCatalogItem>> {
|
|
return request<ListResponse<BaseModelCatalogItem>>('/api/admin/catalog/base-models/reset-all', {
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deleteBaseModel(token: string, baseModelId: string): Promise<void> {
|
|
await request<void>(`/api/admin/catalog/base-models/${baseModelId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function listPricingRules(token: string): Promise<ListResponse<PricingRule>> {
|
|
return request<ListResponse<PricingRule>>('/api/admin/pricing/rules', { token });
|
|
}
|
|
|
|
export async function listPricingRuleSets(token: string): Promise<ListResponse<PricingRuleSet>> {
|
|
return request<ListResponse<PricingRuleSet>>('/api/admin/pricing/rule-sets', { token });
|
|
}
|
|
|
|
export async function createPricingRuleSet(
|
|
token: string,
|
|
input: PricingRuleSetUpsertRequest,
|
|
): Promise<PricingRuleSet> {
|
|
return request<PricingRuleSet>('/api/admin/pricing/rule-sets', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function updatePricingRuleSet(
|
|
token: string,
|
|
ruleSetId: string,
|
|
input: PricingRuleSetUpsertRequest,
|
|
): Promise<PricingRuleSet> {
|
|
return request<PricingRuleSet>(`/api/admin/pricing/rule-sets/${ruleSetId}`, {
|
|
body: input,
|
|
method: 'PATCH',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deletePricingRuleSet(token: string, ruleSetId: string): Promise<void> {
|
|
await request<void>(`/api/admin/pricing/rule-sets/${ruleSetId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function listRuntimePolicySets(token: string): Promise<ListResponse<RuntimePolicySet>> {
|
|
return request<ListResponse<RuntimePolicySet>>('/api/admin/runtime/policy-sets', { token });
|
|
}
|
|
|
|
export async function createRuntimePolicySet(
|
|
token: string,
|
|
input: RuntimePolicySetUpsertRequest,
|
|
): Promise<RuntimePolicySet> {
|
|
return request<RuntimePolicySet>('/api/admin/runtime/policy-sets', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function updateRuntimePolicySet(
|
|
token: string,
|
|
policySetId: string,
|
|
input: RuntimePolicySetUpsertRequest,
|
|
): Promise<RuntimePolicySet> {
|
|
return request<RuntimePolicySet>(`/api/admin/runtime/policy-sets/${policySetId}`, {
|
|
body: input,
|
|
method: 'PATCH',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deleteRuntimePolicySet(token: string, policySetId: string): Promise<void> {
|
|
await request<void>(`/api/admin/runtime/policy-sets/${policySetId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function listTenants(token: string): Promise<ListResponse<GatewayTenant>> {
|
|
return request<ListResponse<GatewayTenant>>('/api/admin/tenants', { token });
|
|
}
|
|
|
|
export async function createTenant(token: string, input: GatewayTenantUpsertRequest): Promise<GatewayTenant> {
|
|
return request<GatewayTenant>('/api/admin/tenants', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function updateTenant(token: string, tenantId: string, input: GatewayTenantUpsertRequest): Promise<GatewayTenant> {
|
|
return request<GatewayTenant>(`/api/admin/tenants/${tenantId}`, {
|
|
body: input,
|
|
method: 'PATCH',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deleteTenant(token: string, tenantId: string): Promise<void> {
|
|
await request<void>(`/api/admin/tenants/${tenantId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function listUsers(token: string): Promise<ListResponse<GatewayUser>> {
|
|
return request<ListResponse<GatewayUser>>('/api/admin/users', { token });
|
|
}
|
|
|
|
export async function createGatewayUser(token: string, input: GatewayUserUpsertRequest): Promise<GatewayUser> {
|
|
return request<GatewayUser>('/api/admin/users', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function updateGatewayUser(token: string, userId: string, input: GatewayUserUpsertRequest): Promise<GatewayUser> {
|
|
return request<GatewayUser>(`/api/admin/users/${userId}`, {
|
|
body: input,
|
|
method: 'PATCH',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deleteGatewayUser(token: string, userId: string): Promise<void> {
|
|
await request<void>(`/api/admin/users/${userId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function listUserGroups(token: string): Promise<ListResponse<UserGroup>> {
|
|
return request<ListResponse<UserGroup>>('/api/admin/user-groups', { token });
|
|
}
|
|
|
|
export async function createUserGroup(token: string, input: UserGroupUpsertRequest): Promise<UserGroup> {
|
|
return request<UserGroup>('/api/admin/user-groups', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function updateUserGroup(token: string, groupId: string, input: UserGroupUpsertRequest): Promise<UserGroup> {
|
|
return request<UserGroup>(`/api/admin/user-groups/${groupId}`, {
|
|
body: input,
|
|
method: 'PATCH',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deleteUserGroup(token: string, groupId: string): Promise<void> {
|
|
await request<void>(`/api/admin/user-groups/${groupId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function listAccessRules(token: string): Promise<ListResponse<GatewayAccessRule>> {
|
|
return request<ListResponse<GatewayAccessRule>>('/api/admin/access-rules', { token });
|
|
}
|
|
|
|
export async function listApiKeyAccessRules(token: string): Promise<ListResponse<GatewayAccessRule>> {
|
|
return request<ListResponse<GatewayAccessRule>>('/api/v1/api-keys/access-rules', { token });
|
|
}
|
|
|
|
export async function createAccessRule(token: string, input: GatewayAccessRuleUpsertRequest): Promise<GatewayAccessRule> {
|
|
return request<GatewayAccessRule>('/api/admin/access-rules', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function batchAccessRules(token: string, input: GatewayAccessRuleBatchRequest): Promise<ListResponse<GatewayAccessRule>> {
|
|
return request<ListResponse<GatewayAccessRule>>('/api/admin/access-rules/batch', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function batchApiKeyAccessRules(token: string, input: GatewayAccessRuleBatchRequest): Promise<ListResponse<GatewayAccessRule>> {
|
|
return request<ListResponse<GatewayAccessRule>>('/api/v1/api-keys/access-rules/batch', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function updateAccessRule(
|
|
token: string,
|
|
ruleId: string,
|
|
input: GatewayAccessRuleUpsertRequest,
|
|
): Promise<GatewayAccessRule> {
|
|
return request<GatewayAccessRule>(`/api/admin/access-rules/${ruleId}`, {
|
|
body: input,
|
|
method: 'PATCH',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deleteAccessRule(token: string, ruleId: string): Promise<void> {
|
|
await request<void>(`/api/admin/access-rules/${ruleId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function listApiKeys(token: string): Promise<ListResponse<GatewayApiKey>> {
|
|
return request<ListResponse<GatewayApiKey>>('/api/v1/api-keys', { token });
|
|
}
|
|
|
|
export async function listPlayableApiKeys(token: string): Promise<ListResponse<PlayableGatewayApiKey>> {
|
|
return request<ListResponse<PlayableGatewayApiKey>>('/api/playground/api-keys', { token });
|
|
}
|
|
|
|
export async function createApiKey(
|
|
token: string,
|
|
input: { name: string; scopes?: string[]; expiresAt?: string },
|
|
): Promise<CreatedGatewayApiKey> {
|
|
return request<CreatedGatewayApiKey>('/api/v1/api-keys', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deleteApiKey(token: string, apiKeyId: string): Promise<void> {
|
|
await request<void>(`/api/v1/api-keys/${apiKeyId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function createPlatform(token: string, input: PlatformCreateInput): Promise<IntegrationPlatform> {
|
|
return request<IntegrationPlatform>('/api/admin/platforms', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function updatePlatform(token: string, platformId: string, input: PlatformCreateInput): Promise<IntegrationPlatform> {
|
|
return request<IntegrationPlatform>(`/api/admin/platforms/${platformId}`, {
|
|
body: input,
|
|
method: 'PATCH',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deletePlatform(token: string, platformId: string): Promise<void> {
|
|
await request<void>(`/api/admin/platforms/${platformId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function createPlatformModel(
|
|
token: string,
|
|
platformId: string,
|
|
input: PlatformModelBindingInput,
|
|
): Promise<PlatformModel> {
|
|
return request<PlatformModel>(`/api/admin/platforms/${platformId}/models`, {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function replacePlatformModels(
|
|
token: string,
|
|
platformId: string,
|
|
models: PlatformModelBindingInput[],
|
|
): Promise<ListResponse<PlatformModel>> {
|
|
return request<ListResponse<PlatformModel>>(`/api/admin/platforms/${platformId}/models`, {
|
|
body: { models },
|
|
method: 'PUT',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function deletePlatformModel(token: string, modelId: string): Promise<void> {
|
|
await request<void>(`/api/admin/platform-models/${modelId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function createChatTask(
|
|
token: string,
|
|
input: { model: string; messages: Array<Record<string, unknown>>; runMode?: string; simulation?: boolean },
|
|
): Promise<{ task: GatewayTask; next: Record<string, string> }> {
|
|
return request<{ task: GatewayTask; next: Record<string, string> }>('/api/v1/chat/completions', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function streamChatCompletions(
|
|
token: string,
|
|
input: { model: string; messages: Array<Record<string, unknown>>; simulation?: boolean },
|
|
onDelta: (delta: string) => void,
|
|
): Promise<void> {
|
|
for await (const delta of streamChatCompletionText(token, input)) {
|
|
onDelta(delta);
|
|
}
|
|
}
|
|
|
|
export async function* streamChatCompletionText(
|
|
token: string,
|
|
input: { model: string; messages: Array<Record<string, unknown>>; simulation?: boolean },
|
|
signal?: AbortSignal,
|
|
): AsyncGenerator<string> {
|
|
const response = await fetch(`${API_BASE}/v1/chat/completions`, {
|
|
body: JSON.stringify({ ...input, stream: true }),
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
method: 'POST',
|
|
signal,
|
|
});
|
|
if (!response.ok) {
|
|
const body = await response.text();
|
|
throw new Error(parseErrorMessage(body) || `Request failed: ${response.status}`);
|
|
}
|
|
if (!response.body) {
|
|
return;
|
|
}
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let buffer = '';
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
buffer += decoder.decode(value, { stream: true });
|
|
const events = buffer.split(/\n\n/);
|
|
buffer = events.pop() ?? '';
|
|
for (const eventBlock of events) {
|
|
const delta = parseSSEBlockDelta(eventBlock);
|
|
if (delta) yield delta;
|
|
}
|
|
}
|
|
if (buffer.trim()) {
|
|
const delta = parseSSEBlockDelta(buffer);
|
|
if (delta) yield delta;
|
|
}
|
|
}
|
|
|
|
export async function createImageGenerationTask(
|
|
token: string,
|
|
input: {
|
|
model: string;
|
|
prompt: string;
|
|
aspect_ratio?: string;
|
|
count?: number;
|
|
height?: number;
|
|
n?: number;
|
|
quality?: string;
|
|
resolution?: string;
|
|
runMode?: string;
|
|
simulation?: boolean;
|
|
size?: string;
|
|
width?: number;
|
|
},
|
|
): Promise<{ task: GatewayTask; next: Record<string, string> }> {
|
|
return request<{ task: GatewayTask; next: Record<string, string> }>('/api/v1/images/generations', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function createImageEditTask(
|
|
token: string,
|
|
input: { model: string; prompt: string; image?: string; mask?: string; runMode?: string; simulation?: boolean },
|
|
): Promise<{ task: GatewayTask; next: Record<string, string> }> {
|
|
return request<{ task: GatewayTask; next: Record<string, string> }>('/api/v1/images/edits', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function createVideoGenerationTask(
|
|
token: string,
|
|
input: {
|
|
model: string;
|
|
prompt: string;
|
|
aspect_ratio?: string;
|
|
count?: number;
|
|
height?: number;
|
|
n?: number;
|
|
resolution?: string;
|
|
runMode?: string;
|
|
simulation?: boolean;
|
|
size?: string;
|
|
width?: number;
|
|
},
|
|
): Promise<{ task: GatewayTask; next: Record<string, string> }> {
|
|
return request<{ task: GatewayTask; next: Record<string, string> }>('/api/v1/videos/generations', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function estimatePricing(
|
|
token: string,
|
|
input: Record<string, unknown>,
|
|
): Promise<{ items: unknown[]; resolver: string }> {
|
|
return request<{ items: unknown[]; resolver: string }>('/api/v1/pricing/estimate', {
|
|
body: input,
|
|
method: 'POST',
|
|
token,
|
|
});
|
|
}
|
|
|
|
export async function getTask(token: string, taskId: string): Promise<GatewayTask> {
|
|
return request<GatewayTask>(`/api/v1/tasks/${taskId}`, { token });
|
|
}
|
|
|
|
export async function listTasks(token: string, query: WorkspaceTaskQuery): Promise<ListResponse<GatewayTask>> {
|
|
const search = new URLSearchParams({
|
|
page: String(query.page),
|
|
pageSize: String(query.pageSize),
|
|
});
|
|
if (query.query) search.set('q', query.query);
|
|
if (query.modelType) search.set('modelType', query.modelType);
|
|
if (query.createdFrom) search.set('createdFrom', query.createdFrom);
|
|
if (query.createdTo) search.set('createdTo', query.createdTo);
|
|
return request<ListResponse<GatewayTask>>(`/api/v1/tasks?${search.toString()}`, { token });
|
|
}
|
|
|
|
export function resolveApiAssetUrl(src: string) {
|
|
if (/^(https?:|data:|blob:)/i.test(src)) return src;
|
|
return `${API_BASE}${src.startsWith('/') ? src : `/${src}`}`;
|
|
}
|
|
|
|
export async function listRateLimitWindows(token: string): Promise<ListResponse<RateLimitWindow>> {
|
|
return request<ListResponse<RateLimitWindow>>('/api/admin/runtime/rate-limit-windows', { token });
|
|
}
|
|
|
|
async function request<T>(
|
|
path: string,
|
|
options: { token?: string; auth?: boolean; method?: string; body?: unknown } = {},
|
|
): Promise<T> {
|
|
const headers: Record<string, string> = {};
|
|
if (options.auth !== false && options.token) {
|
|
headers.Authorization = `Bearer ${options.token}`;
|
|
}
|
|
if (options.body !== undefined) {
|
|
headers['Content-Type'] = 'application/json';
|
|
}
|
|
const response = await fetch(`${API_BASE}${path}`, {
|
|
method: options.method ?? 'GET',
|
|
headers,
|
|
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
});
|
|
if (!response.ok) {
|
|
const body = await response.text();
|
|
throw new Error(parseErrorMessage(body) || `Request failed: ${response.status}`);
|
|
}
|
|
if (response.status === 204) {
|
|
return undefined as T;
|
|
}
|
|
return response.json() as Promise<T>;
|
|
}
|
|
|
|
function parseErrorMessage(body: string) {
|
|
if (!body) {
|
|
return '';
|
|
}
|
|
try {
|
|
const parsed = JSON.parse(body) as { error?: { message?: string } };
|
|
return parsed.error?.message ?? body;
|
|
} catch {
|
|
return body;
|
|
}
|
|
}
|
|
|
|
function parseSSEBlockDelta(block: string) {
|
|
const data = block
|
|
.split(/\n/)
|
|
.filter((line) => line.startsWith('data:'))
|
|
.map((line) => line.replace(/^data:\s?/, ''))
|
|
.join('\n')
|
|
.trim();
|
|
if (!data || data === '[DONE]') return '';
|
|
try {
|
|
const parsed = JSON.parse(data) as {
|
|
choices?: Array<{ delta?: { content?: string }; message?: { content?: string } }>;
|
|
delta?: string;
|
|
output_text?: string;
|
|
};
|
|
return parsed.choices?.[0]?.delta?.content ?? parsed.delta ?? parsed.output_text ?? '';
|
|
} catch {
|
|
return data;
|
|
}
|
|
}
|