diff --git a/.dockerignore b/.dockerignore
index 793f24558..95c09e912 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -4,6 +4,7 @@
node_modules
frontend/node_modules
frontend/dist
+frontend/.tanstack
.axonhub
axonhub
axonhub.db
diff --git a/Dockerfile b/Dockerfile
index a90ac3b83..4cebbcc53 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
FROM --platform=$BUILDPLATFORM node:20-alpine AS frontend-builder
WORKDIR /build
-RUN corepack enable && corepack prepare pnpm@latest --activate
+RUN corepack enable && corepack prepare pnpm@10 --activate
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
diff --git a/frontend/src/features/channels/components/channels-action-dialog.tsx b/frontend/src/features/channels/components/channels-action-dialog.tsx
index 4141e48ce..0e4f78fef 100644
--- a/frontend/src/features/channels/components/channels-action-dialog.tsx
+++ b/frontend/src/features/channels/components/channels-action-dialog.tsx
@@ -1952,6 +1952,7 @@ export function ChannelsActionDialog({ currentRow, duplicateFromRow, open, onOpe
className='min-h-[80px] resize-y pr-10 font-mono text-sm md:col-span-6'
autoComplete='new-password'
data-form-type='other'
+ spellCheck={false}
aria-invalid={!!fieldState.error}
data-testid='channel-api-key-input'
/>
@@ -2028,6 +2029,7 @@ export function ChannelsActionDialog({ currentRow, duplicateFromRow, open, onOpe
className='min-h-[80px] resize-y font-mono text-sm md:col-span-6'
autoComplete='new-password'
data-form-type='other'
+ spellCheck={false}
aria-invalid={!!fieldState.error}
data-testid='channel-api-key-input'
/>
diff --git a/frontend/src/features/channels/data/config_channels.ts b/frontend/src/features/channels/data/config_channels.ts
index 3e319e4b3..dbbaf6c22 100644
--- a/frontend/src/features/channels/data/config_channels.ts
+++ b/frontend/src/features/channels/data/config_channels.ts
@@ -1,5 +1,35 @@
import type { ComponentType } from 'react';
-import { OpenAI, Anthropic, Google, DeepSeek, Doubao, Moonshot, Zhipu, OpenRouter, XAI, Volcengine, SiliconCloud, PPIO, ZAI, LongCat, Minimax, BurnCloud, Vercel, ModelScope, Bailian, Jina, DeepInfra, Github, Claude, Cerebras, XiaomiMiMo, Fireworks, Ollama } from '@lobehub/icons';
+import {
+ OpenAI,
+ Anthropic,
+ Google,
+ DeepSeek,
+ Doubao,
+ Moonshot,
+ Zhipu,
+ OpenRouter,
+ XAI,
+ Volcengine,
+ SiliconCloud,
+ PPIO,
+ ZAI,
+ LongCat,
+ Minimax,
+ BurnCloud,
+ Vercel,
+ ModelScope,
+ Bailian,
+ Jina,
+ DeepInfra,
+ Github,
+ Claude,
+ Cerebras,
+ Qiniu,
+ XiaomiMiMo,
+ Fireworks,
+ Ollama,
+ AiHubMix,
+} from '@lobehub/icons';
import { NanoGPTIcon } from '../components/nanogpt-icon';
import { BURNCLOUD_DEFAULT_MODELS } from './burncloud-models';
import { ApiFormat, ChannelType } from './schema';
diff --git a/frontend/src/features/models/components/data-table-row-actions.tsx b/frontend/src/features/models/components/data-table-row-actions.tsx
index b9a63fb16..162520c21 100644
--- a/frontend/src/features/models/components/data-table-row-actions.tsx
+++ b/frontend/src/features/models/components/data-table-row-actions.tsx
@@ -1,6 +1,6 @@
import { DotsHorizontalIcon } from '@radix-ui/react-icons';
import { Row } from '@tanstack/react-table';
-import { IconEdit, IconArchive, IconTrash, IconNote } from '@tabler/icons-react';
+import { IconEdit, IconArchive, IconTrash, IconNote, IconArchiveOff } from '@tabler/icons-react';
import { useTranslation } from 'react-i18next';
import { usePermissions } from '@/hooks/usePermissions';
import { Button } from '@/components/ui/button';
@@ -75,7 +75,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
{channelPermissions.canRead && }
- {model.status !== 'archived' && (
+ {model.status !== 'archived' ? (
{
setCurrentRow(row.original);
@@ -86,6 +86,17 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
{t('common.buttons.archive')}
+ ) : (
+ {
+ setCurrentRow(row.original);
+ setOpen('archive');
+ }}
+ className='text-green-500!'
+ >
+
+ {t('common.buttons.restore')}
+
)}
{
+ if (!currentRow) return;
+ updateModelStatus.mutate(
+ { id: currentRow.id, status: isArchived ? 'enabled' : 'archived' },
+ { onSuccess: () => setOpen(null) },
+ );
+ };
+
+ const handleClose = () => {
+ setOpen(null);
+ };
+
+ const getDescription = () => {
+ if (!currentRow) return null;
+ const baseDescription = t(isArchived ? 'models.dialogs.status.restoreDescription' : 'models.dialogs.status.archiveDescription', {
+ name: currentRow.name,
+ });
+ const infoText = t(isArchived ? 'models.dialogs.status.restoreInfo' : 'models.dialogs.status.archiveWarning');
+
+ return (
+
+ );
+ };
+
+ return (
+
+ {isArchived ? : }
+ {t(isArchived ? 'models.dialogs.status.restoreTitle' : 'models.dialogs.status.archiveTitle')}
+
+ }
+ desc={getDescription()}
+ confirmText={t(isArchived ? 'common.buttons.restore' : 'common.buttons.archive')}
+ cancelBtnText={t('common.buttons.cancel')}
+ />
+ );
+}
diff --git a/frontend/src/features/models/components/models-dialogs.tsx b/frontend/src/features/models/components/models-dialogs.tsx
index 52163c255..5772f9a58 100644
--- a/frontend/src/features/models/components/models-dialogs.tsx
+++ b/frontend/src/features/models/components/models-dialogs.tsx
@@ -1,5 +1,6 @@
import { useModels } from '../context/models-context';
import { ModelsActionDialog } from './models-action-dialog';
+import { ModelsArchiveDialog } from './models-archive-dialog';
import { ModelsAssociationDialog } from './models-association-dialog';
import { ModelsBatchCreateDialog } from './models-batch-create-dialog';
import { ModelsBulkDisableDialog } from './models-bulk-disable-dialog';
@@ -16,6 +17,7 @@ export function ModelsDialogs() {
{(open === 'create' || open === 'edit') && }
{open === 'batchCreate' && }
{open === 'delete' && }
+ {open === 'archive' && }
{open === 'association' && }
{open === 'settings' && }
{open === 'unassociated' && }
diff --git a/frontend/src/features/models/data/models.ts b/frontend/src/features/models/data/models.ts
index aa87d43be..cbc81aea3 100644
--- a/frontend/src/features/models/data/models.ts
+++ b/frontend/src/features/models/data/models.ts
@@ -582,6 +582,34 @@ export function useDeleteModel() {
});
}
+const UPDATE_MODEL_STATUS_MUTATION = `
+ mutation UpdateModelStatus($id: ID!, $status: ModelStatus!) {
+ updateModelStatus(id: $id, status: $status)
+ }
+`;
+
+export function useUpdateModelStatus() {
+ const { t } = useTranslation();
+ const queryClient = useQueryClient();
+ const { handleError } = useErrorHandler();
+
+ return useMutation({
+ mutationFn: async ({ id, status }: { id: string; status: 'enabled' | 'archived' }) => {
+ const data = await graphqlRequest<{ updateModelStatus: boolean }>(UPDATE_MODEL_STATUS_MUTATION, { id, status });
+ return data.updateModelStatus;
+ },
+ onSuccess: (_data, variables) => {
+ queryClient.invalidateQueries({ queryKey: ['models'] });
+ const statusKey = variables.status === 'archived' ? 'archiveSuccess' : 'restoreSuccess';
+ toast.success(t(`models.messages.${statusKey}`));
+ },
+ onError: (error, variables) => {
+ const contextKey = variables.status === 'archived' ? 'archiveTitle' : 'restoreTitle';
+ handleError(error, { context: t(`models.dialogs.status.${contextKey}`) });
+ },
+ });
+}
+
export function useBulkDisableModels() {
const { t } = useTranslation();
const queryClient = useQueryClient();
diff --git a/frontend/src/locales/en/models.json b/frontend/src/locales/en/models.json
index be2872096..173577ce2 100644
--- a/frontend/src/locales/en/models.json
+++ b/frontend/src/locales/en/models.json
@@ -114,6 +114,12 @@
"models.dialogs.status.enableDescription": "Are you sure you want to enable {{name}}?",
"models.dialogs.status.disableTitle": "Disable Model",
"models.dialogs.status.disableDescription": "Are you sure you want to disable {{name}}?",
+ "models.dialogs.status.archiveTitle": "Archive Model",
+ "models.dialogs.status.archiveDescription": "Are you sure you want to archive {{name}}?",
+ "models.dialogs.status.archiveWarning": "Archived models will not be used for AI requests until they are restored.",
+ "models.dialogs.status.restoreTitle": "Restore Model",
+ "models.dialogs.status.restoreDescription": "Are you sure you want to restore {{name}}?",
+ "models.dialogs.status.restoreInfo": "Restoring this model will re-enable it for AI requests.",
"models.dialogs.bulkDisable.title": "Bulk Disable Models",
"models.dialogs.bulkDisable.description": "Are you sure you want to disable {{count}} models?",
"models.dialogs.bulkDisable.warning": "Disabled models will not be used for AI requests until they are re-enabled.",
@@ -169,6 +175,8 @@
"models.messages.deleteSuccess": "Model deleted successfully",
"models.messages.bulkDisableSuccess": "Successfully disabled {{count}} models",
"models.messages.bulkEnableSuccess": "Successfully enabled {{count}} models",
+ "models.messages.archiveSuccess": "Model archived successfully",
+ "models.messages.restoreSuccess": "Model restored successfully",
"models.developers.alibaba": "Alibaba",
"models.developers.anthropic": "Anthropic",
"models.developers.deepseek": "DeepSeek",
diff --git a/frontend/src/locales/zh-CN/models.json b/frontend/src/locales/zh-CN/models.json
index a741d9a9b..fff838f00 100644
--- a/frontend/src/locales/zh-CN/models.json
+++ b/frontend/src/locales/zh-CN/models.json
@@ -114,6 +114,12 @@
"models.dialogs.status.enableDescription": "确定要启用 {{name}} 吗?",
"models.dialogs.status.disableTitle": "禁用模型",
"models.dialogs.status.disableDescription": "确定要禁用 {{name}} 吗?",
+ "models.dialogs.status.archiveTitle": "归档模型",
+ "models.dialogs.status.archiveDescription": "确定要归档 {{name}} 吗?",
+ "models.dialogs.status.archiveWarning": "归档的模型将不会用于 AI 请求,直到恢复。",
+ "models.dialogs.status.restoreTitle": "恢复模型",
+ "models.dialogs.status.restoreDescription": "确定要恢复 {{name}} 吗?",
+ "models.dialogs.status.restoreInfo": "恢复此模型将重新启用它用于 AI 请求。",
"models.dialogs.bulkDisable.title": "批量禁用模型",
"models.dialogs.bulkDisable.description": "确定要禁用 {{count}} 个模型吗?",
"models.dialogs.bulkDisable.warning": "禁用的模型将不会用于 AI 请求,直到重新启用。",
@@ -169,6 +175,8 @@
"models.messages.deleteSuccess": "模型删除成功",
"models.messages.bulkDisableSuccess": "已成功禁用 {{count}} 个模型",
"models.messages.bulkEnableSuccess": "已成功启用 {{count}} 个模型",
+ "models.messages.archiveSuccess": "模型归档成功",
+ "models.messages.restoreSuccess": "模型恢复成功",
"models.developers.alibaba": "阿里巴巴",
"models.developers.anthropic": "Anthropic",
"models.developers.deepseek": "深度求索",
diff --git a/frontend/src/routes/_authenticated/project/playground/index.tsx b/frontend/src/routes/_authenticated/project/playground/index.tsx
index 6d329e33c..ba1b97e15 100644
--- a/frontend/src/routes/_authenticated/project/playground/index.tsx
+++ b/frontend/src/routes/_authenticated/project/playground/index.tsx
@@ -6,7 +6,7 @@ import Playground from '@/features/playground';
function ProtectedPlayground() {
return (
-
+
diff --git a/internal/ent/internal/schema.go b/internal/ent/internal/schema.go
index b5a0b2bc4..986d3c0a0 100644
--- a/internal/ent/internal/schema.go
+++ b/internal/ent/internal/schema.go
@@ -6,4 +6,4 @@
// Package internal holds a loadable version of the latest schema.
package internal
-const Schema = "{\"Schema\":\"github.com/looplj/axonhub/internal/ent/schema\",\"Package\":\"github.com/looplj/axonhub/internal/ent\",\"Schemas\":[{\"name\":\"APIKey\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"api_keys\",\"unique\":true,\"inverse\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}],\"Skip\":48}}},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"api_keys\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Skip\":32}}},{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"The creator of the API key\"},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":32}},\"comment\":\"Project ID, default to 1 for backward compatibility\"},{\"name\":\"key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"apikey.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"user\",\"V\":\"user\"},{\"N\":\"service_account\",\"V\":\"service_account\"},{\"N\":\"noauth\",\"V\":\"noauth\"}],\"default\":true,\"default_value\":\"user\",\"default_kind\":24,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":32}},\"comment\":\"API Key type: user, service_account, or noauth\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"apikey.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"enabled\",\"default_kind\":24,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}}},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[\"read_channels\",\"write_requests\"],\"default_kind\":23,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"API Key specific scopes. For user type: default read_channels, write_requests (immutable). For service_account: custom scopes.\"},{\"name\":\"profiles\",\"type\":{\"Type\":3,\"Ident\":\"*objects.APIKeyProfiles\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"APIKeyProfiles\",\"Ident\":\"objects.APIKeyProfiles\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"activeProfile\":\"\",\"profiles\":null},\"default_kind\":22,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}}}],\"indexes\":[{\"fields\":[\"user_id\"],\"storage_key\":\"api_keys_by_user_id\"},{\"fields\":[\"project_id\"],\"storage_key\":\"api_keys_by_project_id\"},{\"unique\":true,\"fields\":[\"key\"],\"storage_key\":\"api_keys_by_key\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Channel\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"executions\",\"type\":\"RequestExecution\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"usage_logs\",\"type\":\"UsageLog\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"channel_probes\",\"type\":\"ChannelProbe\",\"annotations\":{\"EntGQL\":{\"Skip\":48}}},{\"name\":\"channel_model_prices\",\"type\":\"ChannelModelPrice\",\"annotations\":{\"EntGQL\":{\"Skip\":48}}},{\"name\":\"provider_quota_status\",\"type\":\"ProviderQuotaStatus\",\"unique\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}],\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"channel.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"openai\",\"V\":\"openai\"},{\"N\":\"openai_responses\",\"V\":\"openai_responses\"},{\"N\":\"codex\",\"V\":\"codex\"},{\"N\":\"vercel\",\"V\":\"vercel\"},{\"N\":\"anthropic\",\"V\":\"anthropic\"},{\"N\":\"anthropic_aws\",\"V\":\"anthropic_aws\"},{\"N\":\"anthropic_gcp\",\"V\":\"anthropic_gcp\"},{\"N\":\"gemini_openai\",\"V\":\"gemini_openai\"},{\"N\":\"gemini\",\"V\":\"gemini\"},{\"N\":\"gemini_vertex\",\"V\":\"gemini_vertex\"},{\"N\":\"deepseek\",\"V\":\"deepseek\"},{\"N\":\"deepseek_anthropic\",\"V\":\"deepseek_anthropic\"},{\"N\":\"deepinfra\",\"V\":\"deepinfra\"},{\"N\":\"fireworks\",\"V\":\"fireworks\"},{\"N\":\"doubao\",\"V\":\"doubao\"},{\"N\":\"doubao_anthropic\",\"V\":\"doubao_anthropic\"},{\"N\":\"moonshot\",\"V\":\"moonshot\"},{\"N\":\"moonshot_anthropic\",\"V\":\"moonshot_anthropic\"},{\"N\":\"zhipu\",\"V\":\"zhipu\"},{\"N\":\"zai\",\"V\":\"zai\"},{\"N\":\"zhipu_anthropic\",\"V\":\"zhipu_anthropic\"},{\"N\":\"zai_anthropic\",\"V\":\"zai_anthropic\"},{\"N\":\"anthropic_fake\",\"V\":\"anthropic_fake\"},{\"N\":\"openai_fake\",\"V\":\"openai_fake\"},{\"N\":\"openrouter\",\"V\":\"openrouter\"},{\"N\":\"xiaomi\",\"V\":\"xiaomi\"},{\"N\":\"xai\",\"V\":\"xai\"},{\"N\":\"ppio\",\"V\":\"ppio\"},{\"N\":\"siliconflow\",\"V\":\"siliconflow\"},{\"N\":\"volcengine\",\"V\":\"volcengine\"},{\"N\":\"longcat\",\"V\":\"longcat\"},{\"N\":\"longcat_anthropic\",\"V\":\"longcat_anthropic\"},{\"N\":\"minimax\",\"V\":\"minimax\"},{\"N\":\"minimax_anthropic\",\"V\":\"minimax_anthropic\"},{\"N\":\"aihubmix\",\"V\":\"aihubmix\"},{\"N\":\"burncloud\",\"V\":\"burncloud\"},{\"N\":\"modelscope\",\"V\":\"modelscope\"},{\"N\":\"bailian\",\"V\":\"bailian\"},{\"N\":\"bailian_anthropic\",\"V\":\"bailian_anthropic\"},{\"N\":\"moonshot_coding\",\"V\":\"moonshot_coding\"},{\"N\":\"jina\",\"V\":\"jina\"},{\"N\":\"github\",\"V\":\"github\"},{\"N\":\"github_copilot\",\"V\":\"github_copilot\"},{\"N\":\"claudecode\",\"V\":\"claudecode\"},{\"N\":\"cerebras\",\"V\":\"cerebras\"},{\"N\":\"antigravity\",\"V\":\"antigravity\"},{\"N\":\"nanogpt\",\"V\":\"nanogpt\"},{\"N\":\"nanogpt_responses\",\"V\":\"nanogpt_responses\"},{\"N\":\"ollama\",\"V\":\"ollama\"}],\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"TYPE\"}}},{\"name\":\"base_url\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"NAME\"}}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"channel.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"disabled\",\"default_kind\":24,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"STATUS\",\"Skip\":16}}},{\"name\":\"credentials\",\"type\":{\"Type\":3,\"Ident\":\"objects.ChannelCredentials\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"ChannelCredentials\",\"Ident\":\"objects.ChannelCredentials\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"GetAllAPIKeys\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"GetEnabledAPIKeys\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]objects.DisabledAPIKey\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"IsOAuth\":{\"In\":[],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"disabled_api_keys\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.DisabledAPIKey\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.DisabledAPIKey\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true,\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"Disabled API keys with metadata (sensitive; requires channel write permission)\"},{\"name\":\"supported_models\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"manual_models\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"auto_sync_supported_models\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"auto_sync_model_pattern\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Regex pattern to filter models during auto-sync. Empty string means no filtering.\"},{\"name\":\"tags\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"default_test_model\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"policies\",\"type\":{\"Type\":3,\"Ident\":\"objects.ChannelPolicies\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"ChannelPolicies\",\"Ident\":\"objects.ChannelPolicies\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"stream\":\"unlimited\"},\"default_kind\":25,\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*objects.ChannelSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"ChannelSettings\",\"Ident\":\"objects.ChannelSettings\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"autoTrimedModelPrefixes\":null,\"extraModelPrefix\":\"\",\"hideMappedModels\":false,\"hideOriginalModels\":false,\"modelMappings\":[],\"overrideHeaders\":null,\"overrideParameters\":\"\",\"transformOptions\":{\"forceArrayInputs\":false,\"forceArrayInstructions\":false,\"replaceDeveloperRoleWithSystem\":false}},\"default_kind\":22,\"position\":{\"Index\":13,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ordering_weight\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":14,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"ORDERING_WEIGHT\"}},\"comment\":\"Ordering weight for display sorting\"},{\"name\":\"error_message\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":15,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":16}}},{\"name\":\"remark\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":16,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"User-defined remark or note for the channel\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\",\"deleted_at\"],\"storage_key\":\"channels_by_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"ChannelModelPrice\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"channel_model_prices\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"versions\",\"type\":\"ChannelModelPriceVersion\"}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"price\",\"type\":{\"Type\":3,\"Ident\":\"objects.ModelPrice\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"ModelPrice\",\"Ident\":\"objects.ModelPrice\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"Equals\":{\"In\":[{\"Name\":\"ModelPrice\",\"Ident\":\"objects.ModelPrice\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"Validate\":{\"In\":[],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The model price, if changed, it will genearte a new reference id.\"},{\"name\":\"reference_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The bill should reference this id.\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"channel_id\",\"model_id\",\"deleted_at\"],\"storage_key\":\"channel_model_prices_by_channel_id_model_id\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"ChannelModelPriceVersion\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"channel_model_price\",\"type\":\"ChannelModelPrice\",\"field\":\"channel_model_price_id\",\"ref_name\":\"versions\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"channel_model_price_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"price\",\"type\":{\"Type\":3,\"Ident\":\"objects.ModelPrice\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"ModelPrice\",\"Ident\":\"objects.ModelPrice\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"Equals\":{\"In\":[{\"Name\":\"ModelPrice\",\"Ident\":\"objects.ModelPrice\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"Validate\":{\"In\":[],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The model price, if changed, it will genearte a new reference id.\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"channelmodelpriceversion.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"effective_start_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The effective start time of the model price.\"},{\"name\":\"effective_end_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The effective end time of the model price, null means it is effective until the next version.\"},{\"name\":\"reference_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"immutable\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The bill should reference this id.\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"ChannelOverrideTemplate\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"channel_override_templates\",\"unique\":true,\"inverse\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}],\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":32}},\"comment\":\"Owner of this template\"},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"validators\":1,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Template name, unique per user\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Template description\"},{\"name\":\"override_parameters\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"Override request body parameters as JSON string\",\"deprecated\":true,\"deprecated_reason\":\"Use body_override_operations instead\"},{\"name\":\"override_headers\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.HeaderEntry\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.HeaderEntry\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"Override request headers\",\"deprecated\":true},{\"name\":\"header_override_operations\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.OverrideOperation\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.OverrideOperation\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}},\"comment\":\"Override request headers\"},{\"name\":\"body_override_operations\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.OverrideOperation\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.OverrideOperation\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}},\"comment\":\"Override request body parameters\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"name\",\"deleted_at\"],\"storage_key\":\"channel_override_templates_by_user_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"ChannelProbe\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"channel_probes\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"total_request_count\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"success_request_count\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"avg_tokens_per_second\",\"type\":{\"Type\":20,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"avg_time_to_first_token_ms\",\"type\":{\"Type\":20,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"immutable\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"timestamp\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"channel_id\",\"timestamp\"],\"storage_key\":\"channel_probes_by_channel_id_timestamp\"}]},{\"name\":\"DataStorage\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"executions\",\"type\":\"RequestExecution\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source name\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source description\"},{\"name\":\"primary\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"data source is primary, only the system database is the primary, it can not be archived.\"},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"datastorage.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"database\",\"V\":\"database\"},{\"N\":\"fs\",\"V\":\"fs\"},{\"N\":\"s3\",\"V\":\"s3\"},{\"N\":\"gcs\",\"V\":\"gcs\"},{\"N\":\"webdav\",\"V\":\"webdav\"}],\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source type\"},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*objects.DataStorageSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"DataStorageSettings\",\"Ident\":\"objects.DataStorageSettings\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source setting\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"datastorage.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"active\",\"default_kind\":24,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source status\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\"],\"storage_key\":\"data_sources_by_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Model\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"developer\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"developer of the model, eg. deeepseek\"},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"model id, eg. deeepseek-chat\"},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"model.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"chat\",\"V\":\"chat\"},{\"N\":\"embedding\",\"V\":\"embedding\"},{\"N\":\"rerank\",\"V\":\"rerank\"},{\"N\":\"image_generation\",\"V\":\"image_generation\"},{\"N\":\"video_generation\",\"V\":\"video_generation\"}],\"default\":true,\"default_value\":\"chat\",\"default_kind\":24,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"model type\"},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"NAME\"}},\"comment\":\"model name, eg. DeepSeek Chat\"},{\"name\":\"icon\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"icon of the model from the lobe-icons, eg. DeepSeek\"},{\"name\":\"group\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"model group, eg. deepseek\"},{\"name\":\"model_card\",\"type\":{\"Type\":3,\"Ident\":\"*objects.ModelCard\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"ModelCard\",\"Ident\":\"objects.ModelCard\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*objects.ModelSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"ModelSettings\",\"Ident\":\"objects.ModelSettings\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"model.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"disabled\",\"default_kind\":24,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":16}}},{\"name\":\"remark\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"User-defined remark or note for the Model\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\",\"deleted_at\"],\"storage_key\":\"models_by_name\"},{\"unique\":true,\"fields\":[\"model_id\",\"deleted_at\"],\"storage_key\":\"models_by_model_id\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Project\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"users\",\"type\":\"User\",\"through\":{\"N\":\"project_users\",\"T\":\"UserProject\"},\"storage_key\":{\"Table\":\"\",\"Symbols\":[\"user_projects_by_user_id_project_id\"],\"Columns\":null},\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"roles\",\"type\":\"Role\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"api_keys\",\"type\":\"APIKey\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"usage_logs\",\"type\":\"UsageLog\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"threads\",\"type\":\"Thread\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"traces\",\"type\":\"Trace\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"prompts\",\"type\":\"Prompt\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"project name\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"project description\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"project.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"active\",\"default_kind\":24,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"project status\"},{\"name\":\"profiles\",\"type\":{\"Type\":3,\"Ident\":\"*objects.ProjectProfiles\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"ProjectProfiles\",\"Ident\":\"objects.ProjectProfiles\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"activeProfile\":\"\",\"profiles\":null},\"default_kind\":22,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}}}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\",\"deleted_at\"],\"storage_key\":\"projects_by_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Prompt\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"projects\",\"type\":\"Project\",\"ref_name\":\"prompts\",\"inverse\":true,\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"Project ID that this prompt belongs to\"},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt name\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt description\"},{\"name\":\"role\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt role\"},{\"name\":\"content\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt content\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"prompt.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"}],\"default\":true,\"default_value\":\"disabled\",\"default_kind\":24,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"order\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"ORDER\"}},\"comment\":\"prompt insertion order, smaller values are inserted first\"},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"objects.PromptSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"PromptSettings\",\"Ident\":\"objects.PromptSettings\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt settings in JSON format\"}],\"indexes\":[{\"fields\":[\"project_id\"],\"storage_key\":\"prompts_by_project_id\"},{\"unique\":true,\"fields\":[\"project_id\",\"name\",\"deleted_at\"],\"storage_key\":\"prompts_by_project_id_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"PromptProtectionRule\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"NAME\"}},\"comment\":\"Rule name\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Rule description\"},{\"name\":\"pattern\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Regex pattern to match prompt content\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"promptprotectionrule.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"disabled\",\"default_kind\":24,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":16}}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*objects.PromptProtectionSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"PromptProtectionSettings\",\"Ident\":\"objects.PromptProtectionSettings\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Prompt protection rule settings\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\",\"deleted_at\"],\"storage_key\":\"prompt_protection_rules_by_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"ProviderQuotaStatus\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"provider_quota_status\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"provider_type\",\"type\":{\"Type\":6,\"Ident\":\"providerquotastatus.ProviderType\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"claudecode\",\"V\":\"claudecode\"},{\"N\":\"codex\",\"V\":\"codex\"},{\"N\":\"github_copilot\",\"V\":\"github_copilot\"},{\"N\":\"nanogpt\",\"V\":\"nanogpt\"}],\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"providerquotastatus.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"available\",\"V\":\"available\"},{\"N\":\"warning\",\"V\":\"warning\"},{\"N\":\"exhausted\",\"V\":\"exhausted\"},{\"N\":\"unknown\",\"V\":\"unknown\"}],\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Overall status: available, warning, exhausted, unknown\"},{\"name\":\"quota_data\",\"type\":{\"Type\":3,\"Ident\":\"map[string]interface {}\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"map[string]interface {}\",\"Kind\":21,\"PkgPath\":\"\",\"Methods\":{}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Provider-specific quota data\"},{\"name\":\"next_reset_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Timestamp for next quota reset (primary window)\"},{\"name\":\"ready\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":true,\"default_kind\":1,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"True if status is available or warning\"},{\"name\":\"next_check_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Timestamp for next scheduled quota check\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"channel_id\"]},{\"fields\":[\"next_check_at\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}]},{\"name\":\"Request\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"api_key\",\"type\":\"APIKey\",\"field\":\"api_key_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"immutable\":true},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"trace\",\"type\":\"Trace\",\"field\":\"trace_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"immutable\":true},{\"name\":\"data_storage\",\"type\":\"DataStorage\",\"field\":\"data_storage_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"immutable\":true},{\"name\":\"executions\",\"type\":\"RequestExecution\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"usage_logs\",\"type\":\"UsageLog\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"api_key_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"API Key ID of the request, null for the request from the Admin.\"},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID, default to 1 for backward compatibility\"},{\"name\":\"trace_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Trace ID that this request belongs to\"},{\"name\":\"data_storage_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Data Storage ID that this request belongs to\"},{\"name\":\"source\",\"type\":{\"Type\":6,\"Ident\":\"request.Source\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"api\",\"V\":\"api\"},{\"N\":\"playground\",\"V\":\"playground\"},{\"N\":\"test\",\"V\":\"test\"}],\"default\":true,\"default_value\":\"api\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"format\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"openai/chat_completions\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"request_headers\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Request headers\"},{\"name\":\"request_body\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"immutable\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"response_body\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"response_chunks\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"external_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":512,\"optional\":true,\"validators\":1,\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"request.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"pending\",\"V\":\"pending\"},{\"N\":\"processing\",\"V\":\"processing\"},{\"N\":\"completed\",\"V\":\"completed\"},{\"N\":\"failed\",\"V\":\"failed\"},{\"N\":\"canceled\",\"V\":\"canceled\"}],\"position\":{\"Index\":13,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"stream\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"immutable\":true,\"position\":{\"Index\":14,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"client_ip\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":15,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_latency_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":16,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_first_token_latency_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":17,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_reasoning_duration_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":18,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Reasoning/thinking duration in milliseconds\"},{\"name\":\"content_saved\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":19,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"whether the generated content has been saved to external storage\"},{\"name\":\"content_storage_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":20,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data storage id used to save the content file\"},{\"name\":\"content_storage_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":21,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"storage key/path of the saved content file\"},{\"name\":\"content_saved_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":22,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"when the content file was saved\"}],\"indexes\":[{\"fields\":[\"api_key_id\",\"created_at\"],\"storage_key\":\"requests_by_api_key_id_created_at\"},{\"fields\":[\"project_id\",\"created_at\"],\"storage_key\":\"requests_by_project_id_created_at\"},{\"fields\":[\"channel_id\",\"created_at\"],\"storage_key\":\"requests_by_channel_id_created_at\"},{\"fields\":[\"trace_id\",\"created_at\"],\"storage_key\":\"requests_by_trace_id_created_at\"},{\"fields\":[\"created_at\"],\"storage_key\":\"requests_by_created_at\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"RequestExecution\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"request\",\"type\":\"Request\",\"field\":\"request_id\",\"ref_name\":\"executions\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"executions\",\"unique\":true,\"inverse\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"data_storage\",\"type\":\"DataStorage\",\"field\":\"data_storage_id\",\"ref_name\":\"executions\",\"unique\":true,\"inverse\":true,\"immutable\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"request_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"data_storage_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Data Storage ID that this request belongs to\"},{\"name\":\"external_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"size\":512,\"optional\":true,\"validators\":1,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"format\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"openai/chat_completions\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"request_body\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"immutable\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"response_body\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"response_chunks\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"error_message\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"response_status_code\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"HTTP status code from the upstream provider\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"requestexecution.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"pending\",\"V\":\"pending\"},{\"N\":\"processing\",\"V\":\"processing\"},{\"N\":\"completed\",\"V\":\"completed\"},{\"N\":\"failed\",\"V\":\"failed\"},{\"N\":\"canceled\",\"V\":\"canceled\"}],\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"stream\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"immutable\":true,\"position\":{\"Index\":13,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_latency_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":14,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_first_token_latency_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":15,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_reasoning_duration_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":16,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Reasoning/thinking duration in milliseconds\"},{\"name\":\"request_headers\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":17,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Request headers\"}],\"indexes\":[{\"fields\":[\"request_id\",\"status\",\"created_at\"],\"storage_key\":\"request_executions_by_request_id_status_created_at\"},{\"fields\":[\"channel_id\"],\"storage_key\":\"request_executions_by_channel_id_created_at\"}],\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"Role\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"users\",\"type\":\"User\",\"ref_name\":\"roles\",\"through\":{\"N\":\"user_roles\",\"T\":\"UserRole\"},\"inverse\":true,\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"roles\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"level\",\"type\":{\"Type\":6,\"Ident\":\"role.Level\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"system\",\"V\":\"system\"},{\"N\":\"project\",\"V\":\"project\"}],\"default\":true,\"default_value\":\"system\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Role level: system or project\"},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID for project-level roles, 0 for system roles, it is used to make the role unique in system level.\"},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Available scopes for this role: write_channels, read_channels, add_users, read_users, etc.\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"project_id\",\"name\"],\"storage_key\":\"roles_by_project_id_name\"},{\"fields\":[\"level\"],\"storage_key\":\"roles_by_level\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"System\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"value\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"mediumtext\"}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Thread\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"threads\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"traces\",\"type\":\"Trace\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID that this thread belongs to\"},{\"name\":\"thread_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Unique thread identifier for this thread\"}],\"indexes\":[{\"fields\":[\"project_id\"],\"storage_key\":\"threads_by_project_id\"},{\"unique\":true,\"fields\":[\"thread_id\"],\"storage_key\":\"threads_by_thread_id\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Trace\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"traces\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"thread\",\"type\":\"Thread\",\"field\":\"thread_id\",\"ref_name\":\"traces\",\"unique\":true,\"inverse\":true,\"immutable\":true},{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID that this trace belongs to\"},{\"name\":\"trace_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Unique trace identifier\"},{\"name\":\"thread_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Thread ID that this trace belongs to\"}],\"indexes\":[{\"fields\":[\"project_id\"],\"storage_key\":\"traces_by_project_id\"},{\"unique\":true,\"fields\":[\"trace_id\"],\"storage_key\":\"traces_by_trace_id\"},{\"fields\":[\"thread_id\"],\"storage_key\":\"traces_by_thread_id\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"UsageLog\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"request\",\"type\":\"Request\",\"field\":\"request_id\",\"ref_name\":\"usage_logs\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"usage_logs\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"usage_logs\",\"unique\":true,\"inverse\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"request_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Related request ID\"},{\"name\":\"api_key_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID, default to 1 for backward compatibility\"},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Channel ID used for the request\"},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Model identifier used for the request\"},{\"name\":\"prompt_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of tokens in the prompt\"},{\"name\":\"completion_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of tokens in the completion\"},{\"name\":\"total_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Total number of tokens used\"},{\"name\":\"prompt_audio_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of audio tokens in the prompt\"},{\"name\":\"prompt_cached_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of cached tokens in the prompt\"},{\"name\":\"prompt_write_cached_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of total write cache tokens, if 5m or 1h ttl variant is present, the field is the sum of 5m and 1h\"},{\"name\":\"prompt_write_cached_tokens_5m\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of token write cache with 5m ttl\"},{\"name\":\"prompt_write_cached_tokens_1h\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of token write cache with 1h ttl\"},{\"name\":\"completion_audio_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":13,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of audio tokens in the completion\"},{\"name\":\"completion_reasoning_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":14,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of reasoning tokens in the completion\"},{\"name\":\"completion_accepted_prediction_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":15,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of accepted prediction tokens\"},{\"name\":\"completion_rejected_prediction_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":16,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of rejected prediction tokens\"},{\"name\":\"source\",\"type\":{\"Type\":6,\"Ident\":\"usagelog.Source\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"api\",\"V\":\"api\"},{\"N\":\"playground\",\"V\":\"playground\"},{\"N\":\"test\",\"V\":\"test\"}],\"default\":true,\"default_value\":\"api\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":17,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Source of the request\"},{\"name\":\"format\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"openai/chat_completions\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":18,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Request format used\"},{\"name\":\"total_cost\",\"type\":{\"Type\":20,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":19,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Total cost calculated based on channel model price\"},{\"name\":\"cost_items\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.CostItem\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.CostItem\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":20,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Detailed cost breakdown items in JSON\"},{\"name\":\"cost_price_reference_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":21,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Reference ID to the channel model price version used for cost calculation\"}],\"indexes\":[{\"fields\":[\"request_id\"],\"storage_key\":\"usage_logs_by_request_id\"},{\"fields\":[\"created_at\"],\"storage_key\":\"usage_logs_by_created_at\"},{\"fields\":[\"model_id\",\"created_at\"],\"storage_key\":\"usage_logs_by_model_id_created_at\"},{\"fields\":[\"project_id\",\"created_at\"],\"storage_key\":\"usage_logs_by_project_id_created_at\"},{\"fields\":[\"channel_id\",\"created_at\"],\"storage_key\":\"usage_logs_by_channel_id_created_at\"},{\"fields\":[\"api_key_id\",\"created_at\"],\"storage_key\":\"usage_logs_by_api_key_id_created_at\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"User\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"projects\",\"type\":\"Project\",\"ref_name\":\"users\",\"through\":{\"N\":\"project_users\",\"T\":\"UserProject\"},\"inverse\":true,\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"api_keys\",\"type\":\"APIKey\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"roles\",\"type\":\"Role\",\"through\":{\"N\":\"user_roles\",\"T\":\"UserRole\"},\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"channel_override_templates\",\"type\":\"ChannelOverrideTemplate\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"email\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"user.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"activated\",\"V\":\"activated\"},{\"N\":\"deactivated\",\"V\":\"deactivated\"}],\"default\":true,\"default_value\":\"activated\",\"default_kind\":24,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"prefer_language\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"en\",\"default_kind\":24,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"用户偏好语言\"},{\"name\":\"password\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"first_name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"last_name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"avatar\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"mediumtext\"},\"comment\":\"用户头像URL\"},{\"name\":\"is_owner\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"User scopes in system level: write_channels, read_channels, add_users, read_users, etc.\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"email\",\"deleted_at\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"UserProject\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"unique\":true,\"required\":true,\"immutable\":true},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"unique\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_owner\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Indicates whether the user is the owner of the project. This field is mutable to allow transferring ownership between users. Only users with sufficient permissions (e.g., current owner) can modify this field.\"},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"User-specific scopes: write_channels, read_channels, add_users, read_users, etc.\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"project_id\"],\"storage_key\":\"user_projects_by_user_id_project_id\"},{\"fields\":[\"project_id\"],\"storage_key\":\"user_projects_by_project_id\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}]},{\"name\":\"UserRole\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"unique\":true,\"required\":true,\"immutable\":true},{\"name\":\"role\",\"type\":\"Role\",\"field\":\"role_id\",\"unique\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"role_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"default\":true,\"default_kind\":19,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"role_id\"],\"storage_key\":\"user_roles_by_user_id_role_id\"},{\"fields\":[\"role_id\"],\"storage_key\":\"user_roles_by_role_id\"}]}],\"Features\":[\"intercept\",\"schema/snapshot\",\"sql/upsert\",\"sql/modifier\",\"entql\",\"privacy\",\"namedges\"]}"
+const Schema = "{\"Schema\":\"github.com/looplj/axonhub/internal/ent/schema\",\"Package\":\"github.com/looplj/axonhub/internal/ent\",\"Schemas\":[{\"name\":\"APIKey\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"api_keys\",\"unique\":true,\"inverse\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}],\"Skip\":48}}},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"api_keys\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Skip\":32}}},{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"The creator of the API key\"},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":32}},\"comment\":\"Project ID, default to 1 for backward compatibility\"},{\"name\":\"key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"apikey.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"user\",\"V\":\"user\"},{\"N\":\"service_account\",\"V\":\"service_account\"},{\"N\":\"noauth\",\"V\":\"noauth\"}],\"default\":true,\"default_value\":\"user\",\"default_kind\":24,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":32}},\"comment\":\"API Key type: user, service_account, or noauth\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"apikey.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"enabled\",\"default_kind\":24,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}}},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[\"read_channels\",\"write_requests\"],\"default_kind\":23,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"API Key specific scopes. For user type: default read_channels, write_requests (immutable). For service_account: custom scopes.\"},{\"name\":\"profiles\",\"type\":{\"Type\":3,\"Ident\":\"*objects.APIKeyProfiles\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"APIKeyProfiles\",\"Ident\":\"objects.APIKeyProfiles\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"activeProfile\":\"\",\"profiles\":null},\"default_kind\":22,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}}}],\"indexes\":[{\"fields\":[\"user_id\"],\"storage_key\":\"api_keys_by_user_id\"},{\"fields\":[\"project_id\"],\"storage_key\":\"api_keys_by_project_id\"},{\"unique\":true,\"fields\":[\"key\"],\"storage_key\":\"api_keys_by_key\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Channel\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"executions\",\"type\":\"RequestExecution\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"usage_logs\",\"type\":\"UsageLog\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"channel_probes\",\"type\":\"ChannelProbe\",\"annotations\":{\"EntGQL\":{\"Skip\":48}}},{\"name\":\"channel_model_prices\",\"type\":\"ChannelModelPrice\",\"annotations\":{\"EntGQL\":{\"Skip\":48}}},{\"name\":\"provider_quota_status\",\"type\":\"ProviderQuotaStatus\",\"unique\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}],\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"channel.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"openai\",\"V\":\"openai\"},{\"N\":\"openai_responses\",\"V\":\"openai_responses\"},{\"N\":\"codex\",\"V\":\"codex\"},{\"N\":\"vercel\",\"V\":\"vercel\"},{\"N\":\"anthropic\",\"V\":\"anthropic\"},{\"N\":\"anthropic_aws\",\"V\":\"anthropic_aws\"},{\"N\":\"anthropic_gcp\",\"V\":\"anthropic_gcp\"},{\"N\":\"gemini_openai\",\"V\":\"gemini_openai\"},{\"N\":\"gemini\",\"V\":\"gemini\"},{\"N\":\"gemini_vertex\",\"V\":\"gemini_vertex\"},{\"N\":\"deepseek\",\"V\":\"deepseek\"},{\"N\":\"deepseek_anthropic\",\"V\":\"deepseek_anthropic\"},{\"N\":\"deepinfra\",\"V\":\"deepinfra\"},{\"N\":\"fireworks\",\"V\":\"fireworks\"},{\"N\":\"doubao\",\"V\":\"doubao\"},{\"N\":\"doubao_anthropic\",\"V\":\"doubao_anthropic\"},{\"N\":\"moonshot\",\"V\":\"moonshot\"},{\"N\":\"moonshot_anthropic\",\"V\":\"moonshot_anthropic\"},{\"N\":\"zhipu\",\"V\":\"zhipu\"},{\"N\":\"zai\",\"V\":\"zai\"},{\"N\":\"zhipu_anthropic\",\"V\":\"zhipu_anthropic\"},{\"N\":\"zai_anthropic\",\"V\":\"zai_anthropic\"},{\"N\":\"anthropic_fake\",\"V\":\"anthropic_fake\"},{\"N\":\"openai_fake\",\"V\":\"openai_fake\"},{\"N\":\"openrouter\",\"V\":\"openrouter\"},{\"N\":\"xiaomi\",\"V\":\"xiaomi\"},{\"N\":\"xai\",\"V\":\"xai\"},{\"N\":\"ppio\",\"V\":\"ppio\"},{\"N\":\"siliconflow\",\"V\":\"siliconflow\"},{\"N\":\"volcengine\",\"V\":\"volcengine\"},{\"N\":\"longcat\",\"V\":\"longcat\"},{\"N\":\"longcat_anthropic\",\"V\":\"longcat_anthropic\"},{\"N\":\"minimax\",\"V\":\"minimax\"},{\"N\":\"minimax_anthropic\",\"V\":\"minimax_anthropic\"},{\"N\":\"aihubmix\",\"V\":\"aihubmix\"},{\"N\":\"burncloud\",\"V\":\"burncloud\"},{\"N\":\"modelscope\",\"V\":\"modelscope\"},{\"N\":\"bailian\",\"V\":\"bailian\"},{\"N\":\"bailian_anthropic\",\"V\":\"bailian_anthropic\"},{\"N\":\"moonshot_coding\",\"V\":\"moonshot_coding\"},{\"N\":\"jina\",\"V\":\"jina\"},{\"N\":\"github\",\"V\":\"github\"},{\"N\":\"github_copilot\",\"V\":\"github_copilot\"},{\"N\":\"claudecode\",\"V\":\"claudecode\"},{\"N\":\"cerebras\",\"V\":\"cerebras\"},{\"N\":\"antigravity\",\"V\":\"antigravity\"},{\"N\":\"nanogpt\",\"V\":\"nanogpt\"},{\"N\":\"nanogpt_responses\",\"V\":\"nanogpt_responses\"},{\"N\":\"ollama\",\"V\":\"ollama\"}],\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"TYPE\"}}},{\"name\":\"base_url\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"NAME\"}}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"channel.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"disabled\",\"default_kind\":24,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"STATUS\",\"Skip\":16}}},{\"name\":\"credentials\",\"type\":{\"Type\":3,\"Ident\":\"objects.ChannelCredentials\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"ChannelCredentials\",\"Ident\":\"objects.ChannelCredentials\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"GetAllAPIKeys\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"GetEnabledAPIKeys\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]objects.DisabledAPIKey\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"IsOAuth\":{\"In\":[],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"disabled_api_keys\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.DisabledAPIKey\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.DisabledAPIKey\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true,\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"Disabled API keys with metadata (sensitive; requires channel write permission)\"},{\"name\":\"supported_models\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"manual_models\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"auto_sync_supported_models\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"auto_sync_model_pattern\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Regex pattern to filter models during auto-sync. Empty string means no filtering.\"},{\"name\":\"tags\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"default_test_model\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"policies\",\"type\":{\"Type\":3,\"Ident\":\"objects.ChannelPolicies\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"ChannelPolicies\",\"Ident\":\"objects.ChannelPolicies\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"stream\":\"unlimited\"},\"default_kind\":25,\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*objects.ChannelSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"ChannelSettings\",\"Ident\":\"objects.ChannelSettings\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"autoTrimedModelPrefixes\":null,\"extraModelPrefix\":\"\",\"hideMappedModels\":false,\"hideOriginalModels\":false,\"modelMappings\":[],\"overrideHeaders\":null,\"overrideParameters\":\"\",\"transformOptions\":{\"forceArrayInputs\":false,\"forceArrayInstructions\":false,\"replaceDeveloperRoleWithSystem\":false}},\"default_kind\":22,\"position\":{\"Index\":13,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ordering_weight\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":14,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"ORDERING_WEIGHT\"}},\"comment\":\"Ordering weight for display sorting\"},{\"name\":\"error_message\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":15,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":16}}},{\"name\":\"remark\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":16,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"User-defined remark or note for the channel\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\",\"deleted_at\"],\"storage_key\":\"channels_by_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"ChannelModelPrice\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"channel_model_prices\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"versions\",\"type\":\"ChannelModelPriceVersion\"}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"price\",\"type\":{\"Type\":3,\"Ident\":\"objects.ModelPrice\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"ModelPrice\",\"Ident\":\"objects.ModelPrice\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"Equals\":{\"In\":[{\"Name\":\"ModelPrice\",\"Ident\":\"objects.ModelPrice\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"Validate\":{\"In\":[],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The model price, if changed, it will genearte a new reference id.\"},{\"name\":\"reference_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The bill should reference this id.\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"channel_id\",\"model_id\",\"deleted_at\"],\"storage_key\":\"channel_model_prices_by_channel_id_model_id\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"ChannelModelPriceVersion\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"channel_model_price\",\"type\":\"ChannelModelPrice\",\"field\":\"channel_model_price_id\",\"ref_name\":\"versions\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"channel_model_price_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"price\",\"type\":{\"Type\":3,\"Ident\":\"objects.ModelPrice\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"ModelPrice\",\"Ident\":\"objects.ModelPrice\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"Equals\":{\"In\":[{\"Name\":\"ModelPrice\",\"Ident\":\"objects.ModelPrice\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"Validate\":{\"In\":[],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The model price, if changed, it will genearte a new reference id.\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"channelmodelpriceversion.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"effective_start_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The effective start time of the model price.\"},{\"name\":\"effective_end_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The effective end time of the model price, null means it is effective until the next version.\"},{\"name\":\"reference_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"immutable\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"The bill should reference this id.\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"ChannelOverrideTemplate\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"ref_name\":\"channel_override_templates\",\"unique\":true,\"inverse\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}],\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":32}},\"comment\":\"Owner of this template\"},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"validators\":1,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Template name, unique per user\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Template description\"},{\"name\":\"override_parameters\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"Override request body parameters as JSON string\",\"deprecated\":true,\"deprecated_reason\":\"Use body_override_operations instead\"},{\"name\":\"override_headers\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.HeaderEntry\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.HeaderEntry\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"Override request headers\",\"deprecated\":true},{\"name\":\"header_override_operations\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.OverrideOperation\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.OverrideOperation\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}},\"comment\":\"Override request headers\"},{\"name\":\"body_override_operations\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.OverrideOperation\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.OverrideOperation\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}},\"comment\":\"Override request body parameters\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"name\",\"deleted_at\"],\"storage_key\":\"channel_override_templates_by_user_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"ChannelProbe\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"channel_probes\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"total_request_count\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"success_request_count\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"avg_tokens_per_second\",\"type\":{\"Type\":20,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"avg_time_to_first_token_ms\",\"type\":{\"Type\":20,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"immutable\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"timestamp\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"fields\":[\"channel_id\",\"timestamp\"],\"storage_key\":\"channel_probes_by_channel_id_timestamp\"}]},{\"name\":\"DataStorage\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"executions\",\"type\":\"RequestExecution\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source name\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source description\"},{\"name\":\"primary\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"data source is primary, only the system database is the primary, it can not be archived.\"},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"datastorage.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"database\",\"V\":\"database\"},{\"N\":\"fs\",\"V\":\"fs\"},{\"N\":\"s3\",\"V\":\"s3\"},{\"N\":\"gcs\",\"V\":\"gcs\"},{\"N\":\"webdav\",\"V\":\"webdav\"}],\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source type\"},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*objects.DataStorageSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"DataStorageSettings\",\"Ident\":\"objects.DataStorageSettings\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source setting\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"datastorage.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"active\",\"default_kind\":24,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data source status\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\"],\"storage_key\":\"data_sources_by_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Model\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"developer\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"developer of the model, eg. deeepseek\"},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"model id, eg. deeepseek-chat\"},{\"name\":\"type\",\"type\":{\"Type\":6,\"Ident\":\"model.Type\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"chat\",\"V\":\"chat\"},{\"N\":\"embedding\",\"V\":\"embedding\"},{\"N\":\"rerank\",\"V\":\"rerank\"},{\"N\":\"image_generation\",\"V\":\"image_generation\"},{\"N\":\"video_generation\",\"V\":\"video_generation\"}],\"default\":true,\"default_value\":\"chat\",\"default_kind\":24,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"model type\"},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"NAME\"}},\"comment\":\"model name, eg. DeepSeek Chat\"},{\"name\":\"icon\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"icon of the model from the lobe-icons, eg. DeepSeek\"},{\"name\":\"group\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"model group, eg. deepseek\"},{\"name\":\"model_card\",\"type\":{\"Type\":3,\"Ident\":\"*objects.ModelCard\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"ModelCard\",\"Ident\":\"objects.ModelCard\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*objects.ModelSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"ModelSettings\",\"Ident\":\"objects.ModelSettings\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"model.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"disabled\",\"default_kind\":24,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":16}}},{\"name\":\"remark\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"User-defined remark or note for the Model\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\",\"deleted_at\"],\"storage_key\":\"models_by_name\"},{\"unique\":true,\"fields\":[\"model_id\",\"deleted_at\"],\"storage_key\":\"models_by_model_id\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Project\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"users\",\"type\":\"User\",\"through\":{\"N\":\"project_users\",\"T\":\"UserProject\"},\"storage_key\":{\"Table\":\"\",\"Symbols\":[\"user_projects_by_user_id_project_id\"],\"Columns\":null},\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"roles\",\"type\":\"Role\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"api_keys\",\"type\":\"APIKey\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"usage_logs\",\"type\":\"UsageLog\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"threads\",\"type\":\"Thread\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"traces\",\"type\":\"Trace\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"prompts\",\"type\":\"Prompt\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"project name\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"project description\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"project.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"active\",\"V\":\"active\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"active\",\"default_kind\":24,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"project status\"},{\"name\":\"profiles\",\"type\":{\"Type\":3,\"Ident\":\"*objects.ProjectProfiles\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"ProjectProfiles\",\"Ident\":\"objects.ProjectProfiles\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":{\"activeProfile\":\"\",\"profiles\":null},\"default_kind\":22,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}}}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\",\"deleted_at\"],\"storage_key\":\"projects_by_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Prompt\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"projects\",\"type\":\"Project\",\"ref_name\":\"prompts\",\"inverse\":true,\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":48}},\"comment\":\"Project ID that this prompt belongs to\"},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt name\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt description\"},{\"name\":\"role\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt role\"},{\"name\":\"content\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt content\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"prompt.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"}],\"default\":true,\"default_value\":\"disabled\",\"default_kind\":24,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"order\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"ORDER\"}},\"comment\":\"prompt insertion order, smaller values are inserted first\"},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"objects.PromptSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":false,\"RType\":{\"Name\":\"PromptSettings\",\"Ident\":\"objects.PromptSettings\",\"Kind\":25,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"prompt settings in JSON format\"}],\"indexes\":[{\"fields\":[\"project_id\"],\"storage_key\":\"prompts_by_project_id\"},{\"unique\":true,\"fields\":[\"project_id\",\"name\",\"deleted_at\"],\"storage_key\":\"prompts_by_project_id_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"PromptProtectionRule\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"NAME\"}},\"comment\":\"Rule name\"},{\"name\":\"description\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Rule description\"},{\"name\":\"pattern\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Regex pattern to match prompt content\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"promptprotectionrule.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"enabled\",\"V\":\"enabled\"},{\"N\":\"disabled\",\"V\":\"disabled\"},{\"N\":\"archived\",\"V\":\"archived\"}],\"default\":true,\"default_value\":\"disabled\",\"default_kind\":24,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Skip\":16}}},{\"name\":\"settings\",\"type\":{\"Type\":3,\"Ident\":\"*objects.PromptProtectionSettings\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"PromptProtectionSettings\",\"Ident\":\"objects.PromptProtectionSettings\",\"Kind\":22,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{}}},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Prompt protection rule settings\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"name\",\"deleted_at\"],\"storage_key\":\"prompt_protection_rules_by_name\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"ProviderQuotaStatus\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"provider_quota_status\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"provider_type\",\"type\":{\"Type\":6,\"Ident\":\"providerquotastatus.ProviderType\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"claudecode\",\"V\":\"claudecode\"},{\"N\":\"codex\",\"V\":\"codex\"},{\"N\":\"github_copilot\",\"V\":\"github_copilot\"},{\"N\":\"nanogpt\",\"V\":\"nanogpt\"}],\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"providerquotastatus.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"available\",\"V\":\"available\"},{\"N\":\"warning\",\"V\":\"warning\"},{\"N\":\"exhausted\",\"V\":\"exhausted\"},{\"N\":\"unknown\",\"V\":\"unknown\"}],\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Overall status: available, warning, exhausted, unknown\"},{\"name\":\"quota_data\",\"type\":{\"Type\":3,\"Ident\":\"map[string]interface {}\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"map[string]interface {}\",\"Kind\":21,\"PkgPath\":\"\",\"Methods\":{}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Provider-specific quota data\"},{\"name\":\"next_reset_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Timestamp for next quota reset (primary window)\"},{\"name\":\"ready\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":true,\"default_kind\":1,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"True if status is available or warning\"},{\"name\":\"next_check_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Timestamp for next scheduled quota check\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"channel_id\"]},{\"fields\":[\"next_check_at\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}]},{\"name\":\"Request\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"api_key\",\"type\":\"APIKey\",\"field\":\"api_key_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"immutable\":true},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"trace\",\"type\":\"Trace\",\"field\":\"trace_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"immutable\":true},{\"name\":\"data_storage\",\"type\":\"DataStorage\",\"field\":\"data_storage_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"immutable\":true},{\"name\":\"executions\",\"type\":\"RequestExecution\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"requests\",\"unique\":true,\"inverse\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"usage_logs\",\"type\":\"UsageLog\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"api_key_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"API Key ID of the request, null for the request from the Admin.\"},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID, default to 1 for backward compatibility\"},{\"name\":\"trace_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Trace ID that this request belongs to\"},{\"name\":\"data_storage_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Data Storage ID that this request belongs to\"},{\"name\":\"source\",\"type\":{\"Type\":6,\"Ident\":\"request.Source\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"api\",\"V\":\"api\"},{\"N\":\"playground\",\"V\":\"playground\"},{\"N\":\"test\",\"V\":\"test\"}],\"default\":true,\"default_value\":\"api\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"format\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"openai/chat_completions\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"request_headers\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Request headers\"},{\"name\":\"request_body\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"immutable\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"response_body\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"response_chunks\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"external_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"text\"}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"request.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"pending\",\"V\":\"pending\"},{\"N\":\"processing\",\"V\":\"processing\"},{\"N\":\"completed\",\"V\":\"completed\"},{\"N\":\"failed\",\"V\":\"failed\"},{\"N\":\"canceled\",\"V\":\"canceled\"}],\"position\":{\"Index\":13,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"stream\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"immutable\":true,\"position\":{\"Index\":14,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"client_ip\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":15,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_latency_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":16,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_first_token_latency_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":17,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_reasoning_duration_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":18,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Reasoning/thinking duration in milliseconds\"},{\"name\":\"content_saved\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":19,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"whether the generated content has been saved to external storage\"},{\"name\":\"content_storage_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":20,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"data storage id used to save the content file\"},{\"name\":\"content_storage_key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":21,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"storage key/path of the saved content file\"},{\"name\":\"content_saved_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":22,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"when the content file was saved\"}],\"indexes\":[{\"fields\":[\"api_key_id\",\"created_at\"],\"storage_key\":\"requests_by_api_key_id_created_at\"},{\"fields\":[\"project_id\",\"created_at\"],\"storage_key\":\"requests_by_project_id_created_at\"},{\"fields\":[\"channel_id\",\"created_at\"],\"storage_key\":\"requests_by_channel_id_created_at\"},{\"fields\":[\"trace_id\",\"created_at\"],\"storage_key\":\"requests_by_trace_id_created_at\"},{\"fields\":[\"created_at\"],\"storage_key\":\"requests_by_created_at\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"RequestExecution\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"request\",\"type\":\"Request\",\"field\":\"request_id\",\"ref_name\":\"executions\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"executions\",\"unique\":true,\"inverse\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"data_storage\",\"type\":\"DataStorage\",\"field\":\"data_storage_id\",\"ref_name\":\"executions\",\"unique\":true,\"inverse\":true,\"immutable\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"request_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"data_storage_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Data Storage ID that this request belongs to\"},{\"name\":\"external_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"text\"}},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"format\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"openai/chat_completions\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"request_body\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"immutable\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"response_body\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"response_chunks\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}},{\"name\":\"error_message\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"response_status_code\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"HTTP status code from the upstream provider\"},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"requestexecution.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"pending\",\"V\":\"pending\"},{\"N\":\"processing\",\"V\":\"processing\"},{\"N\":\"completed\",\"V\":\"completed\"},{\"N\":\"failed\",\"V\":\"failed\"},{\"N\":\"canceled\",\"V\":\"canceled\"}],\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"stream\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"immutable\":true,\"position\":{\"Index\":13,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_latency_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":14,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_first_token_latency_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":15,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"metrics_reasoning_duration_ms\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":16,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Reasoning/thinking duration in milliseconds\"},{\"name\":\"request_headers\",\"type\":{\"Type\":3,\"Ident\":\"objects.JSONRawMessage\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"JSONRawMessage\",\"Ident\":\"objects.JSONRawMessage\",\"Kind\":23,\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"Methods\":{\"MarshalGQL\":{\"In\":[{\"Name\":\"Writer\",\"Ident\":\"io.Writer\",\"Kind\":20,\"PkgPath\":\"io\",\"Methods\":null}],\"Out\":[]},\"MarshalJSON\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalGQL\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalJSON\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":17,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Request headers\"}],\"indexes\":[{\"fields\":[\"request_id\",\"status\",\"created_at\"],\"storage_key\":\"request_executions_by_request_id_status_created_at\"},{\"fields\":[\"channel_id\"],\"storage_key\":\"request_executions_by_channel_id_created_at\"}],\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"Role\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"users\",\"type\":\"User\",\"ref_name\":\"roles\",\"through\":{\"N\":\"user_roles\",\"T\":\"UserRole\"},\"inverse\":true,\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"roles\",\"unique\":true,\"inverse\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"level\",\"type\":{\"Type\":6,\"Ident\":\"role.Level\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"system\",\"V\":\"system\"},{\"N\":\"project\",\"V\":\"project\"}],\"default\":true,\"default_value\":\"system\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Role level: system or project\"},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID for project-level roles, 0 for system roles, it is used to make the role unique in system level.\"},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Available scopes for this role: write_channels, read_channels, add_users, read_users, etc.\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"project_id\",\"name\"],\"storage_key\":\"roles_by_project_id_name\"},{\"fields\":[\"level\"],\"storage_key\":\"roles_by_level\"}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"System\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"key\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"value\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"mediumtext\"}}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Thread\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"threads\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"traces\",\"type\":\"Trace\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID that this thread belongs to\"},{\"name\":\"thread_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Unique thread identifier for this thread\"}],\"indexes\":[{\"fields\":[\"project_id\"],\"storage_key\":\"threads_by_project_id\"},{\"unique\":true,\"fields\":[\"thread_id\"],\"storage_key\":\"threads_by_thread_id\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"Trace\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"traces\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"thread\",\"type\":\"Thread\",\"field\":\"thread_id\",\"ref_name\":\"traces\",\"unique\":true,\"inverse\":true,\"immutable\":true},{\"name\":\"requests\",\"type\":\"Request\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID that this trace belongs to\"},{\"name\":\"trace_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"unique\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Unique trace identifier\"},{\"name\":\"thread_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Thread ID that this trace belongs to\"}],\"indexes\":[{\"fields\":[\"project_id\"],\"storage_key\":\"traces_by_project_id\"},{\"unique\":true,\"fields\":[\"trace_id\"],\"storage_key\":\"traces_by_trace_id\"},{\"fields\":[\"thread_id\"],\"storage_key\":\"traces_by_thread_id\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"UsageLog\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"request\",\"type\":\"Request\",\"field\":\"request_id\",\"ref_name\":\"usage_logs\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"ref_name\":\"usage_logs\",\"unique\":true,\"inverse\":true,\"required\":true,\"immutable\":true},{\"name\":\"channel\",\"type\":\"Channel\",\"field\":\"channel_id\",\"ref_name\":\"usage_logs\",\"unique\":true,\"inverse\":true,\"immutable\":true,\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"request_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Related request ID\"},{\"name\":\"api_key_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":1,\"default_kind\":2,\"immutable\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Project ID, default to 1 for backward compatibility\"},{\"name\":\"channel_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"immutable\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Channel ID used for the request\"},{\"name\":\"model_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Model identifier used for the request\"},{\"name\":\"prompt_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of tokens in the prompt\"},{\"name\":\"completion_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of tokens in the completion\"},{\"name\":\"total_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Total number of tokens used\"},{\"name\":\"prompt_audio_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of audio tokens in the prompt\"},{\"name\":\"prompt_cached_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of cached tokens in the prompt\"},{\"name\":\"prompt_write_cached_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of total write cache tokens, if 5m or 1h ttl variant is present, the field is the sum of 5m and 1h\"},{\"name\":\"prompt_write_cached_tokens_5m\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":11,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of token write cache with 5m ttl\"},{\"name\":\"prompt_write_cached_tokens_1h\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":12,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of token write cache with 1h ttl\"},{\"name\":\"completion_audio_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":13,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of audio tokens in the completion\"},{\"name\":\"completion_reasoning_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":14,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of reasoning tokens in the completion\"},{\"name\":\"completion_accepted_prediction_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":15,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of accepted prediction tokens\"},{\"name\":\"completion_rejected_prediction_tokens\",\"type\":{\"Type\":13,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"default\":true,\"default_value\":0,\"default_kind\":6,\"position\":{\"Index\":16,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Number of rejected prediction tokens\"},{\"name\":\"source\",\"type\":{\"Type\":6,\"Ident\":\"usagelog.Source\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"api\",\"V\":\"api\"},{\"N\":\"playground\",\"V\":\"playground\"},{\"N\":\"test\",\"V\":\"test\"}],\"default\":true,\"default_value\":\"api\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":17,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Source of the request\"},{\"name\":\"format\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"openai/chat_completions\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":18,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Request format used\"},{\"name\":\"total_cost\",\"type\":{\"Type\":20,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":19,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Total cost calculated based on channel model price\"},{\"name\":\"cost_items\",\"type\":{\"Type\":3,\"Ident\":\"[]objects.CostItem\",\"PkgPath\":\"github.com/looplj/axonhub/internal/objects\",\"PkgName\":\"objects\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]objects.CostItem\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":20,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Detailed cost breakdown items in JSON\"},{\"name\":\"cost_price_reference_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":21,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Reference ID to the channel model price version used for cost calculation\"}],\"indexes\":[{\"fields\":[\"request_id\"],\"storage_key\":\"usage_logs_by_request_id\"},{\"fields\":[\"created_at\"],\"storage_key\":\"usage_logs_by_created_at\"},{\"fields\":[\"model_id\",\"created_at\"],\"storage_key\":\"usage_logs_by_model_id_created_at\"},{\"fields\":[\"project_id\",\"created_at\"],\"storage_key\":\"usage_logs_by_project_id_created_at\"},{\"fields\":[\"channel_id\",\"created_at\"],\"storage_key\":\"usage_logs_by_channel_id_created_at\"},{\"fields\":[\"api_key_id\",\"created_at\"],\"storage_key\":\"usage_logs_by_api_key_id_created_at\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"User\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"projects\",\"type\":\"Project\",\"ref_name\":\"users\",\"through\":{\"N\":\"project_users\",\"T\":\"UserProject\"},\"inverse\":true,\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"api_keys\",\"type\":\"APIKey\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}},{\"name\":\"roles\",\"type\":\"Role\",\"through\":{\"N\":\"user_roles\",\"T\":\"UserRole\"},\"annotations\":{\"EntGQL\":{\"RelayConnection\":true}}},{\"name\":\"channel_override_templates\",\"type\":\"ChannelOverrideTemplate\",\"annotations\":{\"EntGQL\":{\"RelayConnection\":true,\"Skip\":48}}}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"deleted_at\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":0,\"default_kind\":2,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1},\"annotations\":{\"EntGQL\":{\"Skip\":63}}},{\"name\":\"email\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"status\",\"type\":{\"Type\":6,\"Ident\":\"user.Status\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"enums\":[{\"N\":\"activated\",\"V\":\"activated\"},{\"N\":\"deactivated\",\"V\":\"deactivated\"}],\"default\":true,\"default_value\":\"activated\",\"default_kind\":24,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"prefer_language\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"en\",\"default_kind\":24,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"用户偏好语言\"},{\"name\":\"password\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"first_name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"last_name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"\",\"default_kind\":24,\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"avatar\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"schema_type\":{\"mysql\":\"mediumtext\"},\"comment\":\"用户头像URL\"},{\"name\":\"is_owner\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"User scopes in system level: write_channels, read_channels, add_users, read_users, etc.\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"email\",\"deleted_at\"]}],\"hooks\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"interceptors\":[{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":1}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}],\"annotations\":{\"EntGQL\":{\"MutationInputs\":[{\"IsCreate\":true},{}],\"QueryField\":{},\"RelayConnection\":true}}},{\"name\":\"UserProject\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"unique\":true,\"required\":true,\"immutable\":true},{\"name\":\"project\",\"type\":\"Project\",\"field\":\"project_id\",\"unique\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"project_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"is_owner\",\"type\":{\"Type\":1,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":false,\"default_kind\":1,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Indicates whether the user is the owner of the project. This field is mutable to allow transferring ownership between users. Only users with sufficient permissions (e.g., current owner) can modify this field.\"},{\"name\":\"scopes\",\"type\":{\"Type\":3,\"Ident\":\"[]string\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":true,\"RType\":{\"Name\":\"\",\"Ident\":\"[]string\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":{}}},\"optional\":true,\"default\":true,\"default_value\":[],\"default_kind\":23,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"User-specific scopes: write_channels, read_channels, add_users, read_users, etc.\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"project_id\"],\"storage_key\":\"user_projects_by_user_id_project_id\"},{\"fields\":[\"project_id\"],\"storage_key\":\"user_projects_by_project_id\"}],\"policy\":[{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}]},{\"name\":\"UserRole\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"user\",\"type\":\"User\",\"field\":\"user_id\",\"unique\":true,\"required\":true,\"immutable\":true},{\"name\":\"role\",\"type\":\"Role\",\"field\":\"role_id\",\"unique\":true,\"required\":true,\"immutable\":true}],\"fields\":[{\"name\":\"user_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"role_id\",\"type\":{\"Type\":12,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"immutable\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"created_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"default\":true,\"default_kind\":19,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"CREATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}},{\"name\":\"updated_at\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"OrderField\":\"UPDATED_AT\",\"Skip\":48},\"EntSQL\":{\"default_expr\":\"CURRENT_TIMESTAMP\"}}}],\"indexes\":[{\"unique\":true,\"fields\":[\"user_id\",\"role_id\"],\"storage_key\":\"user_roles_by_user_id_role_id\"},{\"fields\":[\"role_id\"],\"storage_key\":\"user_roles_by_role_id\"}]}],\"Features\":[\"intercept\",\"schema/snapshot\",\"sql/upsert\",\"sql/modifier\",\"entql\",\"privacy\",\"namedges\"]}"
diff --git a/internal/ent/migrate/schema.go b/internal/ent/migrate/schema.go
index fd857dc73..eb7be5ea9 100644
--- a/internal/ent/migrate/schema.go
+++ b/internal/ent/migrate/schema.go
@@ -418,7 +418,7 @@ var (
{Name: "request_body", Type: field.TypeJSON},
{Name: "response_body", Type: field.TypeJSON, Nullable: true},
{Name: "response_chunks", Type: field.TypeJSON, Nullable: true},
- {Name: "external_id", Type: field.TypeString, Nullable: true, Size: 512},
+ {Name: "external_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"mysql": "text"}},
{Name: "status", Type: field.TypeEnum, Enums: []string{"pending", "processing", "completed", "failed", "canceled"}},
{Name: "stream", Type: field.TypeBool, Default: false},
{Name: "client_ip", Type: field.TypeString, Default: ""},
@@ -506,7 +506,7 @@ var (
{Name: "created_at", Type: field.TypeTime, Default: schema.Expr("CURRENT_TIMESTAMP")},
{Name: "updated_at", Type: field.TypeTime, Default: schema.Expr("CURRENT_TIMESTAMP")},
{Name: "project_id", Type: field.TypeInt, Default: 1},
- {Name: "external_id", Type: field.TypeString, Nullable: true, Size: 512},
+ {Name: "external_id", Type: field.TypeString, Nullable: true, SchemaType: map[string]string{"mysql": "text"}},
{Name: "model_id", Type: field.TypeString},
{Name: "format", Type: field.TypeString, Default: "openai/chat_completions"},
{Name: "request_body", Type: field.TypeJSON},
diff --git a/internal/ent/request/request.go b/internal/ent/request/request.go
index 084434898..597707141 100644
--- a/internal/ent/request/request.go
+++ b/internal/ent/request/request.go
@@ -193,8 +193,6 @@ var (
DefaultProjectID int
// DefaultFormat holds the default value on creation for the "format" field.
DefaultFormat string
- // ExternalIDValidator is a validator for the "external_id" field. It is called by the builders before save.
- ExternalIDValidator func(string) error
// DefaultStream holds the default value on creation for the "stream" field.
DefaultStream bool
// DefaultClientIP holds the default value on creation for the "client_ip" field.
diff --git a/internal/ent/request_create.go b/internal/ent/request_create.go
index d4761a197..9d5e3f26b 100644
--- a/internal/ent/request_create.go
+++ b/internal/ent/request_create.go
@@ -487,11 +487,6 @@ func (_c *RequestCreate) check() error {
if _, ok := _c.mutation.RequestBody(); !ok {
return &ValidationError{Name: "request_body", err: errors.New(`ent: missing required field "Request.request_body"`)}
}
- if v, ok := _c.mutation.ExternalID(); ok {
- if err := request.ExternalIDValidator(v); err != nil {
- return &ValidationError{Name: "external_id", err: fmt.Errorf(`ent: validator failed for field "Request.external_id": %w`, err)}
- }
- }
if _, ok := _c.mutation.Status(); !ok {
return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "Request.status"`)}
}
diff --git a/internal/ent/request_update.go b/internal/ent/request_update.go
index f19c55fdf..c7f19030e 100644
--- a/internal/ent/request_update.go
+++ b/internal/ent/request_update.go
@@ -442,11 +442,6 @@ func (_u *RequestUpdate) defaults() error {
// check runs all checks and user-defined validators on the builder.
func (_u *RequestUpdate) check() error {
- if v, ok := _u.mutation.ExternalID(); ok {
- if err := request.ExternalIDValidator(v); err != nil {
- return &ValidationError{Name: "external_id", err: fmt.Errorf(`ent: validator failed for field "Request.external_id": %w`, err)}
- }
- }
if v, ok := _u.mutation.Status(); ok {
if err := request.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Request.status": %w`, err)}
@@ -1134,11 +1129,6 @@ func (_u *RequestUpdateOne) defaults() error {
// check runs all checks and user-defined validators on the builder.
func (_u *RequestUpdateOne) check() error {
- if v, ok := _u.mutation.ExternalID(); ok {
- if err := request.ExternalIDValidator(v); err != nil {
- return &ValidationError{Name: "external_id", err: fmt.Errorf(`ent: validator failed for field "Request.external_id": %w`, err)}
- }
- }
if v, ok := _u.mutation.Status(); ok {
if err := request.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Request.status": %w`, err)}
diff --git a/internal/ent/requestexecution/requestexecution.go b/internal/ent/requestexecution/requestexecution.go
index 44dbcd5aa..cec50f62b 100644
--- a/internal/ent/requestexecution/requestexecution.go
+++ b/internal/ent/requestexecution/requestexecution.go
@@ -132,8 +132,6 @@ var (
UpdateDefaultUpdatedAt func() time.Time
// DefaultProjectID holds the default value on creation for the "project_id" field.
DefaultProjectID int
- // ExternalIDValidator is a validator for the "external_id" field. It is called by the builders before save.
- ExternalIDValidator func(string) error
// DefaultFormat holds the default value on creation for the "format" field.
DefaultFormat string
// DefaultStream holds the default value on creation for the "stream" field.
diff --git a/internal/ent/requestexecution_create.go b/internal/ent/requestexecution_create.go
index af22082e6..8603ffb54 100644
--- a/internal/ent/requestexecution_create.go
+++ b/internal/ent/requestexecution_create.go
@@ -330,11 +330,6 @@ func (_c *RequestExecutionCreate) check() error {
if _, ok := _c.mutation.RequestID(); !ok {
return &ValidationError{Name: "request_id", err: errors.New(`ent: missing required field "RequestExecution.request_id"`)}
}
- if v, ok := _c.mutation.ExternalID(); ok {
- if err := requestexecution.ExternalIDValidator(v); err != nil {
- return &ValidationError{Name: "external_id", err: fmt.Errorf(`ent: validator failed for field "RequestExecution.external_id": %w`, err)}
- }
- }
if _, ok := _c.mutation.ModelID(); !ok {
return &ValidationError{Name: "model_id", err: errors.New(`ent: missing required field "RequestExecution.model_id"`)}
}
diff --git a/internal/ent/requestexecution_update.go b/internal/ent/requestexecution_update.go
index bb1331ddc..af31151bc 100644
--- a/internal/ent/requestexecution_update.go
+++ b/internal/ent/requestexecution_update.go
@@ -296,11 +296,6 @@ func (_u *RequestExecutionUpdate) defaults() {
// check runs all checks and user-defined validators on the builder.
func (_u *RequestExecutionUpdate) check() error {
- if v, ok := _u.mutation.ExternalID(); ok {
- if err := requestexecution.ExternalIDValidator(v); err != nil {
- return &ValidationError{Name: "external_id", err: fmt.Errorf(`ent: validator failed for field "RequestExecution.external_id": %w`, err)}
- }
- }
if v, ok := _u.mutation.Status(); ok {
if err := requestexecution.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "RequestExecution.status": %w`, err)}
@@ -717,11 +712,6 @@ func (_u *RequestExecutionUpdateOne) defaults() {
// check runs all checks and user-defined validators on the builder.
func (_u *RequestExecutionUpdateOne) check() error {
- if v, ok := _u.mutation.ExternalID(); ok {
- if err := requestexecution.ExternalIDValidator(v); err != nil {
- return &ValidationError{Name: "external_id", err: fmt.Errorf(`ent: validator failed for field "RequestExecution.external_id": %w`, err)}
- }
- }
if v, ok := _u.mutation.Status(); ok {
if err := requestexecution.StatusValidator(v); err != nil {
return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "RequestExecution.status": %w`, err)}
diff --git a/internal/ent/runtime/runtime.go b/internal/ent/runtime/runtime.go
index 35a1bcbbd..1263c7b1c 100644
--- a/internal/ent/runtime/runtime.go
+++ b/internal/ent/runtime/runtime.go
@@ -526,10 +526,6 @@ func init() {
requestDescFormat := requestFields[6].Descriptor()
// request.DefaultFormat holds the default value on creation for the format field.
request.DefaultFormat = requestDescFormat.Default.(string)
- // requestDescExternalID is the schema descriptor for external_id field.
- requestDescExternalID := requestFields[12].Descriptor()
- // request.ExternalIDValidator is a validator for the "external_id" field. It is called by the builders before save.
- request.ExternalIDValidator = requestDescExternalID.Validators[0].(func(string) error)
// requestDescStream is the schema descriptor for stream field.
requestDescStream := requestFields[14].Descriptor()
// request.DefaultStream holds the default value on creation for the stream field.
@@ -561,10 +557,6 @@ func init() {
requestexecutionDescProjectID := requestexecutionFields[0].Descriptor()
// requestexecution.DefaultProjectID holds the default value on creation for the project_id field.
requestexecution.DefaultProjectID = requestexecutionDescProjectID.Default.(int)
- // requestexecutionDescExternalID is the schema descriptor for external_id field.
- requestexecutionDescExternalID := requestexecutionFields[4].Descriptor()
- // requestexecution.ExternalIDValidator is a validator for the "external_id" field. It is called by the builders before save.
- requestexecution.ExternalIDValidator = requestexecutionDescExternalID.Validators[0].(func(string) error)
// requestexecutionDescFormat is the schema descriptor for format field.
requestexecutionDescFormat := requestexecutionFields[6].Descriptor()
// requestexecution.DefaultFormat holds the default value on creation for the format field.
diff --git a/internal/ent/schema/request.go b/internal/ent/schema/request.go
index c98fdb17b..c57a4f480 100644
--- a/internal/ent/schema/request.go
+++ b/internal/ent/schema/request.go
@@ -3,6 +3,7 @@ package schema
import (
"entgo.io/contrib/entgql"
"entgo.io/ent"
+ "entgo.io/ent/dialect"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
@@ -84,7 +85,9 @@ func (Request) Fields() []ent.Field {
// External ID for tracking requests in external systems
field.String("external_id").
Optional().
- MaxLen(512),
+ SchemaType(map[string]string{
+ dialect.MySQL: "text",
+ }),
// The status of the request.
field.Enum("status").Values("pending", "processing", "completed", "failed", "canceled"),
// Whether the request is a streaming request
diff --git a/internal/ent/schema/request_execution.go b/internal/ent/schema/request_execution.go
index 722fd5f35..d0988c692 100644
--- a/internal/ent/schema/request_execution.go
+++ b/internal/ent/schema/request_execution.go
@@ -3,6 +3,7 @@ package schema
import (
"entgo.io/contrib/entgql"
"entgo.io/ent"
+ "entgo.io/ent/dialect"
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
@@ -43,7 +44,9 @@ func (RequestExecution) Fields() []ent.Field {
// External ID for tracking requests in external systems
field.String("external_id").
Optional().
- MaxLen(512),
+ SchemaType(map[string]string{
+ dialect.MySQL: "text",
+ }),
field.String("model_id").Immutable(),
// The format of the request, e.g: openai/chat_completions, claude/messages, openai/response.
field.String("format").Immutable().Default("openai/chat_completions"),
diff --git a/internal/ent/schema/user.go b/internal/ent/schema/user.go
index b98389f8a..fa910f59f 100644
--- a/internal/ent/schema/user.go
+++ b/internal/ent/schema/user.go
@@ -101,7 +101,6 @@ func (User) Policy() ent.Policy {
Mutation: scopes.MutationPolicy{
scopes.OwnerRule(),
scopes.UserWriteScopeRule(scopes.ScopeWriteUsers),
- scopes.UserOwnedMutationRule(),
},
}
}
diff --git a/internal/server/api/openai.go b/internal/server/api/openai.go
index e013fb971..8d6afdc34 100644
--- a/internal/server/api/openai.go
+++ b/internal/server/api/openai.go
@@ -574,15 +574,19 @@ func (handlers *OpenAIHandlers) ListModels(c *gin.Context) {
return m.ID
})
- dbModels, err := handlers.EntClient.Model.Query().
- Where(
- model.StatusEQ(model.StatusEnabled),
- model.ModelIDIn(visibleIDs...),
- ).
- All(ctx)
- if err != nil {
- handlers.writeOpenAIInternalError(c, requestID, err)
- return
+ var dbModels []*ent.Model
+ for _, chunk := range lo.Chunk(visibleIDs, biz.SQLiteMaxVariableLimit) {
+ chunkModels, err := handlers.EntClient.Model.Query().
+ Where(
+ model.StatusEQ(model.StatusEnabled),
+ model.ModelIDIn(chunk...),
+ ).
+ All(ctx)
+ if err != nil {
+ handlers.writeOpenAIInternalError(c, requestID, err)
+ return
+ }
+ dbModels = append(dbModels, chunkModels...)
}
dbModelMap := make(map[string]*ent.Model, len(dbModels))
diff --git a/internal/server/biz/channel_metrics.go b/internal/server/biz/channel_metrics.go
index 53cdf4f8b..f0322bd71 100644
--- a/internal/server/biz/channel_metrics.go
+++ b/internal/server/biz/channel_metrics.go
@@ -98,9 +98,9 @@ type channelMetricsResult struct {
func (svc *ChannelService) loadAllChannelMetricsFromExecutions(ctx context.Context, client *ent.Client, since time.Time) (map[int]*channelMetricsResult, error) {
// Single query to get request count and last failure time for all channels
type queryResult struct {
- ChannelID int `json:"channel_id"`
- RequestCount int64 `json:"request_count"`
- LastFailureAt time.Time `json:"last_failure_at"`
+ ChannelID int `json:"channel_id"`
+ RequestCount int64 `json:"request_count"`
+ LastFailureAt *time.Time `json:"last_failure_at"`
}
var results []queryResult
@@ -112,8 +112,8 @@ func (svc *ChannelService) loadAllChannelMetricsFromExecutions(ctx context.Conte
requestexecution.StatusNotIn(requestexecution.StatusPending, requestexecution.StatusProcessing),
).
Modify(func(s *sql.Selector) {
- // Use a subquery or join to get last failure time per channel
- // For simplicity, we use MAX(CASE WHEN status = 'failed' THEN created_at END) to get last failure
+ // Use MAX(CASE WHEN ...) to get last failure time per channel.
+ // Returns NULL when there are no failed requests, which maps to nil for *time.Time.
s.Select(
s.C(requestexecution.FieldChannelID),
sql.As(sql.Count("*"), "request_count"),
@@ -133,8 +133,8 @@ func (svc *ChannelService) loadAllChannelMetricsFromExecutions(ctx context.Conte
ChannelID: r.ChannelID,
RequestCount: r.RequestCount,
}
- if !r.LastFailureAt.IsZero() {
- m.LastFailureAt = &r.LastFailureAt
+ if r.LastFailureAt != nil {
+ m.LastFailureAt = r.LastFailureAt
}
metricsMap[r.ChannelID] = m
diff --git a/internal/server/biz/model.go b/internal/server/biz/model.go
index 04dff0aa4..f143a84a7 100644
--- a/internal/server/biz/model.go
+++ b/internal/server/biz/model.go
@@ -39,6 +39,12 @@ func NewModelService(params ModelServiceParams) *ModelService {
}
}
+// SQLiteMaxVariableLimit is a safe chunk size for SQL IN clauses.
+// SQLite versions prior to 3.32.0 have a limit of 999 variables;
+// newer versions support 32766. We use 500 to stay safely below both limits
+// and leave room for other query parameters.
+const SQLiteMaxVariableLimit = 500
+
type ModelService struct {
*AbstractService
@@ -312,22 +318,31 @@ func (svc *ModelService) BulkCreateModels(ctx context.Context, inputs []*ent.Cre
inputMap[key] = true
}
- // Check if any models already exist
- existingModels, err := svc.entFromContext(ctx).Model.Query().
- Where(func(s *sql.Selector) {
- var predicates []*sql.Predicate
- for _, input := range inputs {
- predicates = append(predicates, sql.And(
- sql.EQ(model.FieldDeveloper, input.Developer),
- sql.EQ(model.FieldModelID, input.ModelID),
- ))
- }
+ // Check if any models already exist (chunk inputs to avoid SQLite variable limit;
+ // each predicate uses 2 variables: developer + modelID).
+ chunkSize := SQLiteMaxVariableLimit / 2
- s.Where(sql.Or(predicates...))
- }).
- All(ctx)
- if err != nil {
- return nil, fmt.Errorf("failed to check existing models: %w", err)
+ var existingModels []*ent.Model
+
+ for _, chunk := range lo.Chunk(inputs, chunkSize) {
+ chunkModels, err := svc.entFromContext(ctx).Model.Query().
+ Where(func(s *sql.Selector) {
+ var predicates []*sql.Predicate
+ for _, input := range chunk {
+ predicates = append(predicates, sql.And(
+ sql.EQ(model.FieldDeveloper, input.Developer),
+ sql.EQ(model.FieldModelID, input.ModelID),
+ ))
+ }
+
+ s.Where(sql.Or(predicates...))
+ }).
+ All(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to check existing models: %w", err)
+ }
+
+ existingModels = append(existingModels, chunkModels...)
}
if len(existingModels) > 0 {
@@ -430,51 +445,63 @@ func (svc *ModelService) DeleteModel(ctx context.Context, id int) error {
}
// BulkArchiveModels archives multiple models by their IDs.
+// IDs are chunked to avoid SQLite "too many SQL variables" error.
func (svc *ModelService) BulkArchiveModels(ctx context.Context, ids []int) error {
- _, err := svc.entFromContext(ctx).Model.Update().
- Where(model.IDIn(ids...)).
- SetStatus(model.StatusArchived).
- Save(ctx)
- if err != nil {
- return fmt.Errorf("failed to bulk archive models: %w", err)
+ for _, chunk := range lo.Chunk(ids, SQLiteMaxVariableLimit) {
+ _, err := svc.entFromContext(ctx).Model.Update().
+ Where(model.IDIn(chunk...)).
+ SetStatus(model.StatusArchived).
+ Save(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to bulk archive models: %w", err)
+ }
}
return nil
}
// BulkDisableModels disables multiple models by their IDs.
+// IDs are chunked to avoid SQLite "too many SQL variables" error.
func (svc *ModelService) BulkDisableModels(ctx context.Context, ids []int) error {
- _, err := svc.entFromContext(ctx).Model.Update().
- Where(model.IDIn(ids...)).
- SetStatus(model.StatusDisabled).
- Save(ctx)
- if err != nil {
- return fmt.Errorf("failed to bulk disable models: %w", err)
+ for _, chunk := range lo.Chunk(ids, SQLiteMaxVariableLimit) {
+ _, err := svc.entFromContext(ctx).Model.Update().
+ Where(model.IDIn(chunk...)).
+ SetStatus(model.StatusDisabled).
+ Save(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to bulk disable models: %w", err)
+ }
}
return nil
}
// BulkEnableModels enables multiple models by their IDs.
+// IDs are chunked to avoid SQLite "too many SQL variables" error.
func (svc *ModelService) BulkEnableModels(ctx context.Context, ids []int) error {
- _, err := svc.entFromContext(ctx).Model.Update().
- Where(model.IDIn(ids...)).
- SetStatus(model.StatusEnabled).
- Save(ctx)
- if err != nil {
- return fmt.Errorf("failed to bulk enable models: %w", err)
+ for _, chunk := range lo.Chunk(ids, SQLiteMaxVariableLimit) {
+ _, err := svc.entFromContext(ctx).Model.Update().
+ Where(model.IDIn(chunk...)).
+ SetStatus(model.StatusEnabled).
+ Save(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to bulk enable models: %w", err)
+ }
}
return nil
}
// BulkDeleteModels deletes multiple models by their IDs.
+// IDs are chunked to avoid SQLite "too many SQL variables" error.
func (svc *ModelService) BulkDeleteModels(ctx context.Context, ids []int) error {
- _, err := svc.entFromContext(ctx).Model.Delete().
- Where(model.IDIn(ids...)).
- Exec(ctx)
- if err != nil {
- return fmt.Errorf("failed to bulk delete models: %w", err)
+ for _, chunk := range lo.Chunk(ids, SQLiteMaxVariableLimit) {
+ _, err := svc.entFromContext(ctx).Model.Delete().
+ Where(model.IDIn(chunk...)).
+ Exec(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to bulk delete models: %w", err)
+ }
}
return nil
@@ -651,17 +678,32 @@ func (svc *ModelService) ListEnabledModels(ctx context.Context) ([]ModelFacade,
// queryConfiguredModelFacades queries enabled Model entities and returns them as ModelFacades
// filtered by allowed model IDs and channel associations.
func (svc *ModelService) queryConfiguredModelFacades(ctx context.Context, allowedModelIDs []string, channels []*Channel) ([]ModelFacade, error) {
- query := svc.entFromContext(ctx).
- Model.
- Query().
- Where(model.StatusEQ(model.StatusEnabled))
+ var enabledModels []*ent.Model
+
if len(allowedModelIDs) > 0 {
- query = query.Where(model.ModelIDIn(allowedModelIDs...))
- }
+ // Chunk allowedModelIDs to avoid SQLite "too many SQL variables" error.
+ for _, chunk := range lo.Chunk(allowedModelIDs, SQLiteMaxVariableLimit) {
+ chunkModels, err := svc.entFromContext(ctx).Model.Query().
+ Where(
+ model.StatusEQ(model.StatusEnabled),
+ model.ModelIDIn(chunk...),
+ ).
+ All(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to list configured models: %w", err)
+ }
- enabledModels, err := query.All(ctx)
- if err != nil {
- return nil, fmt.Errorf("failed to list configured models: %w", err)
+ enabledModels = append(enabledModels, chunkModels...)
+ }
+ } else {
+ var err error
+
+ enabledModels, err = svc.entFromContext(ctx).Model.Query().
+ Where(model.StatusEQ(model.StatusEnabled)).
+ All(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to list configured models: %w", err)
+ }
}
var models []ModelFacade
diff --git a/internal/server/gc/gc.go b/internal/server/gc/gc.go
index 4ebf1412b..f307bb915 100644
--- a/internal/server/gc/gc.go
+++ b/internal/server/gc/gc.go
@@ -571,8 +571,7 @@ func (w *Worker) runVacuum(ctx context.Context) error {
vacuumSQL = "VACUUM"
}
- _, err := sqlDriver.ExecContext(ctx, vacuumSQL, nil, nil)
- if err != nil {
+ if _, err := sqlDriver.ExecContext(ctx, vacuumSQL); err != nil {
return fmt.Errorf("failed to execute %s: %w", vacuumSQL, err)
}
diff --git a/internal/server/gc/vacuum_test.go b/internal/server/gc/vacuum_test.go
new file mode 100644
index 000000000..16cf172c7
--- /dev/null
+++ b/internal/server/gc/vacuum_test.go
@@ -0,0 +1,81 @@
+package gc
+
+import (
+ "context"
+ "database/sql"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ _ "github.com/jackc/pgx/v5/stdlib"
+
+ entsql "entgo.io/ent/dialect/sql"
+
+ "github.com/looplj/axonhub/internal/ent"
+ "github.com/looplj/axonhub/internal/ent/enttest"
+)
+
+func TestWorker_runVacuum_Disabled(t *testing.T) {
+ t.Parallel()
+
+ w := &Worker{Config: Config{VacuumEnabled: false}}
+ require.NoError(t, w.runVacuum(context.Background()))
+}
+
+func TestWorker_runVacuum_SQLite(t *testing.T) {
+ t.Parallel()
+
+ // Distinct DSN per subtest so the in-memory DBs don't collide.
+ for _, full := range []bool{false, true} {
+ name := "vacuum"
+ if full {
+ name = "vacuum_full_ignored_on_sqlite"
+ }
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ dsn := "file:vac_" + name + "?mode=memory&_fk=1"
+ client := enttest.NewEntClient(t, "sqlite3", dsn)
+ t.Cleanup(func() { _ = client.Close() })
+
+ w := &Worker{Ent: client, Config: Config{VacuumEnabled: true, VacuumFull: full}}
+ require.NoError(t, w.runVacuum(context.Background()))
+ })
+ }
+}
+
+// TestWorker_runVacuum_Postgres exercises the real pgx code path that previously
+// failed with "mismatched param and argument count". Gated on AXONHUB_TEST_PG_DSN
+// because the project has no in-process Postgres harness.
+func TestWorker_runVacuum_Postgres(t *testing.T) {
+ dsn := os.Getenv("AXONHUB_TEST_PG_DSN")
+ if dsn == "" {
+ t.Skip("AXONHUB_TEST_PG_DSN not set; skipping real-Postgres VACUUM check")
+ }
+
+ for _, full := range []bool{false, true} {
+ name := "vacuum"
+ if full {
+ name = "vacuum_full"
+ }
+ t.Run(name, func(t *testing.T) {
+ client := newPostgresEntClient(t, dsn)
+ t.Cleanup(func() { _ = client.Close() })
+
+ w := &Worker{Ent: client, Config: Config{VacuumEnabled: true, VacuumFull: full}}
+ require.NoError(t, w.runVacuum(context.Background()))
+ })
+ }
+}
+
+func newPostgresEntClient(t *testing.T, dsn string) *ent.Client {
+ t.Helper()
+
+ sqlDB, err := sql.Open("pgx", dsn)
+ require.NoError(t, err)
+
+ require.NoError(t, sqlDB.PingContext(context.Background()))
+
+ return ent.NewClient(ent.Driver(entsql.OpenDB("postgres", sqlDB)))
+}
diff --git a/internal/server/gql/graphql.go b/internal/server/gql/graphql.go
index a71e37780..8a78d5626 100644
--- a/internal/server/gql/graphql.go
+++ b/internal/server/gql/graphql.go
@@ -7,6 +7,7 @@ import (
"net/http"
"entgo.io/contrib/entgql"
+ "entgo.io/ent/privacy"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/extension"
@@ -144,6 +145,15 @@ func NewGraphqlHandlers(deps Dependencies) *GraphqlHandler {
},
}
}
+ // Convert ent privacy deny errors to FORBIDDEN
+ if errors.Is(err, privacy.Deny) {
+ return &gqlerror.Error{
+ Message: "permission denied",
+ Extensions: map[string]any{
+ "code": xerrors.ErrCodeForbidden,
+ },
+ }
+ }
// Return default error presentation
return graphql.DefaultErrorPresenter(ctx, err)
})
diff --git a/internal/server/static/dist/.gitkeep b/internal/server/static/dist/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/llm/transformer/anthropic/claudecode/outbound_test.go b/llm/transformer/anthropic/claudecode/outbound_test.go
index 02cd8c5b8..1e8e6ee4a 100644
--- a/llm/transformer/anthropic/claudecode/outbound_test.go
+++ b/llm/transformer/anthropic/claudecode/outbound_test.go
@@ -183,10 +183,13 @@ func TestClaudeCodeTransformer_TransformRequest(t *testing.T) {
httpReq, err := transformer.TransformRequest(ctx, req)
require.NoError(t, err)
- // Should have generated user ID
+ // Should have generated user ID (may be sanitized to a hash if too long)
userID := gjson.GetBytes(httpReq.Body, "metadata.user_id").String()
assert.NotEmpty(t, userID)
- assert.NotNil(t, ParseUserID(userID))
+ if parsed := ParseUserID(userID); parsed == nil {
+ // Sanitized format: "h_" prefix followed by hex hash
+ assert.True(t, strings.HasPrefix(userID, "h_"), "user_id should be either parseable v2 format or sanitized h_ hash")
+ }
})
t.Run("does not add billing cch when not official", func(t *testing.T) {
diff --git a/llm/transformer/anthropic/outbound_convert.go b/llm/transformer/anthropic/outbound_convert.go
index 117386530..21d4a8efc 100644
--- a/llm/transformer/anthropic/outbound_convert.go
+++ b/llm/transformer/anthropic/outbound_convert.go
@@ -6,6 +6,7 @@ import (
"github.com/looplj/axonhub/llm"
"github.com/looplj/axonhub/llm/internal/pkg/xjson"
"github.com/looplj/axonhub/llm/internal/pkg/xurl"
+ "github.com/looplj/axonhub/llm/transformer/openai"
"github.com/looplj/axonhub/llm/transformer/shared"
)
@@ -122,7 +123,7 @@ func buildBaseRequest(chatReq *llm.Request, config *Config) *MessageRequest {
}
if chatReq.Metadata != nil && chatReq.Metadata["user_id"] != "" {
- req.Metadata = &AnthropicMetadata{UserID: chatReq.Metadata["user_id"]}
+ req.Metadata = &AnthropicMetadata{UserID: openai.SanitizeUserStr(chatReq.Metadata["user_id"])}
}
// Determine thinking config priority: adaptive > enabled > disabled
diff --git a/llm/transformer/doubao/outbound.go b/llm/transformer/doubao/outbound.go
index 8092cc9c3..08d074755 100644
--- a/llm/transformer/doubao/outbound.go
+++ b/llm/transformer/doubao/outbound.go
@@ -142,7 +142,7 @@ func (t *OutboundTransformer) TransformRequest(
}
if llmReq.Metadata != nil {
- doubaoReq.UserID = llmReq.Metadata["user_id"]
+ doubaoReq.UserID = openai.SanitizeUserStr(llmReq.Metadata["user_id"])
doubaoReq.RequestID = llmReq.Metadata["request_id"]
}
diff --git a/llm/transformer/ollama/outbound.go b/llm/transformer/ollama/outbound.go
index c3be5090e..6ff9ead13 100644
--- a/llm/transformer/ollama/outbound.go
+++ b/llm/transformer/ollama/outbound.go
@@ -47,6 +47,7 @@ type ChatRequest struct {
type ChatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
+ Images []string `json:"images,omitempty"`
Thinking string `json:"thinking,omitempty"`
}
@@ -101,10 +102,10 @@ func (t *OutboundTransformer) TransformRequest(ctx context.Context, llmReq *llm.
}
for _, msg := range llmReq.Messages {
- content := getContentString(msg.Content)
ollamaReq.Messages = append(ollamaReq.Messages, ChatMessage{
Role: msg.Role,
- Content: content,
+ Content: getContentString(msg.Content),
+ Images: getImages(msg.Content),
})
}
@@ -158,6 +159,35 @@ func getContentString(content llm.MessageContent) string {
return ""
}
+func getImages(content llm.MessageContent) []string {
+ if len(content.MultipleContent) == 0 {
+ return nil
+ }
+
+ images := make([]string, 0)
+
+ for _, part := range content.MultipleContent {
+ if part.Type != "image_url" || part.ImageURL == nil {
+ continue
+ }
+
+ image := strings.TrimSpace(part.ImageURL.URL)
+ if image == "" {
+ continue
+ }
+
+ if comma := strings.Index(image, ","); strings.HasPrefix(image, "data:") && comma >= 0 {
+ image = image[comma+1:]
+ }
+
+ if image != "" {
+ images = append(images, image)
+ }
+ }
+
+ return images
+}
+
func (t *OutboundTransformer) buildOptions(llmReq *llm.Request) *Options {
var opts *Options
diff --git a/llm/transformer/ollama/outbound_test.go b/llm/transformer/ollama/outbound_test.go
new file mode 100644
index 000000000..a5f3bd44b
--- /dev/null
+++ b/llm/transformer/ollama/outbound_test.go
@@ -0,0 +1,81 @@
+package ollama
+
+import (
+ "context"
+ "encoding/json"
+ "testing"
+
+ "github.com/samber/lo"
+ "github.com/stretchr/testify/require"
+
+ "github.com/looplj/axonhub/llm"
+)
+
+func TestTransformRequestPreservesImageURLParts(t *testing.T) {
+ transformer, err := NewOutboundTransformerWithConfig(&Config{
+ BaseURL: "http://localhost:11434",
+ })
+ require.NoError(t, err)
+
+ req, err := transformer.TransformRequest(context.Background(), &llm.Request{
+ Model: "qwen3.5:4b-q4_K_M",
+ Messages: []llm.Message{
+ {
+ Role: "user",
+ Content: llm.MessageContent{
+ MultipleContent: []llm.MessageContentPart{
+ {
+ Type: "text",
+ Text: lo.ToPtr("OCR this image."),
+ },
+ {
+ Type: "image_url",
+ ImageURL: &llm.ImageURL{
+ URL: "data:image/png;base64,Zm9v",
+ },
+ },
+ },
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+
+ var got ChatRequest
+ require.NoError(t, json.Unmarshal(req.Body, &got))
+ require.Len(t, got.Messages, 1)
+ require.Equal(t, "OCR this image.", got.Messages[0].Content)
+ require.Equal(t, []string{"Zm9v"}, got.Messages[0].Images)
+}
+
+func TestTransformRequestPreservesPlainImageURLParts(t *testing.T) {
+ transformer, err := NewOutboundTransformerWithConfig(&Config{
+ BaseURL: "http://localhost:11434",
+ })
+ require.NoError(t, err)
+
+ req, err := transformer.TransformRequest(context.Background(), &llm.Request{
+ Model: "vision-model",
+ Messages: []llm.Message{
+ {
+ Role: "user",
+ Content: llm.MessageContent{
+ MultipleContent: []llm.MessageContentPart{
+ {
+ Type: "image_url",
+ ImageURL: &llm.ImageURL{
+ URL: "https://example.com/image.png",
+ },
+ },
+ },
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+
+ var got ChatRequest
+ require.NoError(t, json.Unmarshal(req.Body, &got))
+ require.Len(t, got.Messages, 1)
+ require.Equal(t, []string{"https://example.com/image.png"}, got.Messages[0].Images)
+}
diff --git a/llm/transformer/openai/codex/constants.go b/llm/transformer/openai/codex/constants.go
index 02437e871..1a255a2ab 100644
--- a/llm/transformer/openai/codex/constants.go
+++ b/llm/transformer/openai/codex/constants.go
@@ -19,6 +19,7 @@ func DefaultModels() []string {
"gpt-5.3-codex-spark",
"gpt-5.4",
"gpt-5.4-mini",
+ "gpt-5.5",
}
}
diff --git a/llm/transformer/openai/embedding.go b/llm/transformer/openai/embedding.go
index acd327b1b..4ef1032ce 100644
--- a/llm/transformer/openai/embedding.go
+++ b/llm/transformer/openai/embedding.go
@@ -54,7 +54,7 @@ func (t *OutboundTransformer) transformEmbeddingRequest(
Model: llmReq.Model,
EncodingFormat: llmReq.Embedding.EncodingFormat,
Dimensions: llmReq.Embedding.Dimensions,
- User: llmReq.Embedding.User,
+ User: sanitizeUserStr(llmReq.Embedding.User),
}
// Re-marshal to JSON (ensure clean output)
diff --git a/llm/transformer/openai/image_outbound.go b/llm/transformer/openai/image_outbound.go
index 8c95db34e..b52733ef9 100644
--- a/llm/transformer/openai/image_outbound.go
+++ b/llm/transformer/openai/image_outbound.go
@@ -343,11 +343,12 @@ func (t *OutboundTransformer) buildImageEditRequest(chatReq *llm.Request, apiKey
// Add user if specified
if chatReq.Image.User != "" {
- if err := writer.WriteField("user", chatReq.Image.User); err != nil {
+ sanitized := sanitizeUserStr(chatReq.Image.User)
+ if err := writer.WriteField("user", sanitized); err != nil {
return nil, fmt.Errorf("failed to write user field: %w", err)
}
- jsonBody["user"] = chatReq.Image.User
+ jsonBody["user"] = sanitized
}
if err := writer.Close(); err != nil {
diff --git a/llm/transformer/openai/outbound_convert.go b/llm/transformer/openai/outbound_convert.go
index 549c0aba0..d97ffda06 100644
--- a/llm/transformer/openai/outbound_convert.go
+++ b/llm/transformer/openai/outbound_convert.go
@@ -1,11 +1,50 @@
package openai
import (
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+
"github.com/samber/lo"
"github.com/looplj/axonhub/llm"
)
+// maxUserLen is the maximum allowed length for the user field.
+// Some upstream providers (e.g., Zhipu) reject user_id values longer than 128 characters.
+const maxUserLen = 128
+
+// SanitizeUser ensures the user string does not exceed maxUserLen characters.
+// If the input exceeds the limit, it returns a deterministic SHA256-based
+// identifier prefixed with "h_" so it remains unique and reproducible.
+// Returns nil unchanged.
+func SanitizeUser(user *string) *string {
+ if user == nil || *user == "" {
+ return user
+ }
+ s := SanitizeUserStr(*user)
+ return &s
+}
+
+// SanitizeUserStr is the string variant of SanitizeUser.
+func SanitizeUserStr(user string) string {
+ if user == "" || len(user) <= maxUserLen {
+ return user
+ }
+ h := sha256.Sum256([]byte(user))
+ hexStr := hex.EncodeToString(h[:])
+ if len(hexStr) > maxUserLen-2 {
+ hexStr = hexStr[:maxUserLen-2]
+ }
+ return fmt.Sprintf("h_%s", hexStr)
+}
+
+// sanitizeUser is an alias for package-internal use.
+func sanitizeUser(user *string) *string { return SanitizeUser(user) }
+
+// sanitizeUserStr is an alias for package-internal use.
+func sanitizeUserStr(user string) string { return SanitizeUserStr(user) }
+
// RequestFromLLM creates OpenAI Request from unified llm.Request.
func RequestFromLLM(r *llm.Request) *Request {
if r == nil {
@@ -26,7 +65,7 @@ func RequestFromLLM(r *llm.Request) *Request {
TopP: r.TopP,
PromptCacheKey: r.PromptCacheKey,
SafetyIdentifier: r.SafetyIdentifier,
- User: r.User,
+ User: sanitizeUser(r.User),
LogitBias: r.LogitBias,
Metadata: r.Metadata,
Modalities: r.Modalities,
diff --git a/llm/transformer/openai/responses/outbound.go b/llm/transformer/openai/responses/outbound.go
index 784fb2079..c9cbeafcf 100644
--- a/llm/transformer/openai/responses/outbound.go
+++ b/llm/transformer/openai/responses/outbound.go
@@ -14,6 +14,7 @@ import (
"github.com/looplj/axonhub/llm/httpclient"
"github.com/looplj/axonhub/llm/internal/pkg/xmap"
"github.com/looplj/axonhub/llm/transformer"
+ "github.com/looplj/axonhub/llm/transformer/openai"
"github.com/looplj/axonhub/llm/transformer/shared"
)
@@ -170,7 +171,7 @@ func (t *OutboundTransformer) TransformRequest(ctx context.Context, llmReq *llm.
Store: llmReq.Store,
ServiceTier: llmReq.ServiceTier,
SafetyIdentifier: llmReq.SafetyIdentifier,
- User: llmReq.User,
+ User: openai.SanitizeUser(llmReq.User),
Metadata: llmReq.Metadata,
MaxOutputTokens: llmReq.MaxCompletionTokens,
TopLogprobs: llmReq.TopLogprobs,
diff --git a/llm/transformer/zai/outbound.go b/llm/transformer/zai/outbound.go
index 3d6c2c522..aeb00fb50 100644
--- a/llm/transformer/zai/outbound.go
+++ b/llm/transformer/zai/outbound.go
@@ -124,7 +124,7 @@ func (t *OutboundTransformer) TransformRequest(
}
if llmReq.Metadata != nil {
- zaiReq.UserID = llmReq.Metadata["user_id"]
+ zaiReq.UserID = openai.SanitizeUserStr(llmReq.Metadata["user_id"])
zaiReq.RequestID = llmReq.Metadata["request_id"]
}