File size: 4,738 Bytes
f1dd159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import type { ModelRouteDTO } from "@/entities/model/types";
import { apiRequest, type PaginatedDTO } from "@/shared/api/client";
import { createObjectDecoder, createPaginatedDecoder, decodeBooleanResult, decodeCountResult, hasShape, isArrayOf, isBoolean, isNumber, isOneOf, isOptional, isString } from "@/shared/api/decoder";
import type { SortOrder } from "@/shared/lib/table-sort";

type ListModelsInput = {
  page: number;
  pageSize: number;
  search?: string;
  status?: string;
  provider?: "grok_build" | "grok_web" | "grok_console" | "";
  providerScope?: Array<"grok_build" | "grok_web" | "grok_console">;
  tierScope?: Array<"free" | "super">;
  activeScope?: boolean;
  sortBy?: string;
  sortOrder?: SortOrder;
};

const modelRouteValidator = hasShape({
  id: isString,
  publicId: isString,
  provider: isOneOf("grok_build", "grok_web", "grok_console"),
  upstreamModel: isString,
  capability: isOneOf("responses", "chat", "image", "image_edit", "video"),
  origin: isOneOf("catalog", "discovered", "manual"),
  enabled: isBoolean,
  accountIds: isArrayOf(isString),
  bindingMode: isBoolean,
  supportedAccounts: isNumber,
  syncedAccounts: isNumber,
  totalAccounts: isNumber,
  capabilityKnown: isBoolean,
  available: isBoolean,
  lastSyncedAt: isOptional(isString),
});
const decodeModelRoute = createObjectDecoder<ModelRouteDTO>("model route", {
  id: isString, publicId: isString, provider: isOneOf("grok_build", "grok_web", "grok_console"), upstreamModel: isString,
  capability: isOneOf("responses", "chat", "image", "image_edit", "video"), origin: isOneOf("catalog", "discovered", "manual"),
  enabled: isBoolean, accountIds: isArrayOf(isString), bindingMode: isBoolean, supportedAccounts: isNumber,
  syncedAccounts: isNumber, totalAccounts: isNumber, capabilityKnown: isBoolean, available: isBoolean, lastSyncedAt: isOptional(isString),
});
const decodeModelPage = createPaginatedDecoder<ModelRouteDTO>(modelRouteValidator);
const modelAccountValidator = hasShape({ id: isString, name: isString });
const decodeModelAccounts = createObjectDecoder<{ items: ModelAccountOptionDTO[] }>("model accounts", { items: isArrayOf(modelAccountValidator) });

export function listModels(input: ListModelsInput): Promise<PaginatedDTO<ModelRouteDTO>> {
  const query = new URLSearchParams({ page: String(input.page), pageSize: String(input.pageSize) });
  if (input.search) query.set("search", input.search);
  if (input.status) query.set("status", input.status);
  if (input.provider) query.set("provider", input.provider);
  for (const value of input.providerScope ?? []) query.append("providerScope", value);
  for (const value of input.tierScope ?? []) query.append("tierScope", value);
  if (input.activeScope) query.set("activeScope", "true");
  if (input.sortBy && input.sortOrder) {
    query.set("sortBy", input.sortBy);
    query.set("sortOrder", input.sortOrder);
  }
  return apiRequest(`/api/admin/v1/models?${query}`, {}, decodeModelPage);
}

export function syncModels(): Promise<{ synced: number }> {
  return apiRequest("/api/admin/v1/models/sync", { method: "POST" }, decodeCountResult<{ synced: number }>("synced"));
}

export type ModelAccountOptionDTO = { id: string; name: string };

export type CreateModelInput = {
  publicId: string;
  provider: ModelRouteDTO["provider"];
  upstreamModel: string;
  capability: ModelRouteDTO["capability"];
  enabled: boolean;
  accountIds: string[];
};

export function listModelAccountOptions(provider: ModelRouteDTO["provider"]): Promise<{ items: ModelAccountOptionDTO[] }> {
  return apiRequest(`/api/admin/v1/models/accounts?provider=${provider}`, {}, decodeModelAccounts);
}

export function createModel(input: CreateModelInput): Promise<ModelRouteDTO> {
  return apiRequest("/api/admin/v1/models", { method: "POST", body: input }, decodeModelRoute);
}

export function updateModel(id: string, input: { publicId: string; enabled: boolean; accountIds: string[] }): Promise<ModelRouteDTO> {
  return apiRequest(`/api/admin/v1/models/${id}`, { method: "PATCH", body: input }, decodeModelRoute);
}

export function deleteModel(id: string): Promise<{ deleted: boolean }> {
  return apiRequest(`/api/admin/v1/models/${id}`, { method: "DELETE" }, decodeBooleanResult<{ deleted: boolean }>("deleted"));
}

export function deleteModels(ids: string[]): Promise<{ deleted: number }> {
  return apiRequest("/api/admin/v1/models", { method: "DELETE", body: { ids } }, decodeCountResult<{ deleted: number }>("deleted"));
}

export function updateModelsEnabled(ids: string[], enabled: boolean): Promise<{ updated: number }> {
  return apiRequest("/api/admin/v1/models/batch", { method: "PATCH", body: { ids, enabled } }, decodeCountResult<{ updated: number }>("updated"));
}