diff --git a/backend/drizzle/0013_flowery_maria_hill.sql b/backend/drizzle/0013_flowery_maria_hill.sql
new file mode 100644
index 0000000..a7d2307
--- /dev/null
+++ b/backend/drizzle/0013_flowery_maria_hill.sql
@@ -0,0 +1,35 @@
+CREATE TYPE "public"."alert_channel_type" AS ENUM('webhook', 'email', 'feishu');--> statement-breakpoint
+CREATE TYPE "public"."alert_history_status" AS ENUM('sent', 'failed', 'suppressed');--> statement-breakpoint
+CREATE TYPE "public"."alert_rule_type" AS ENUM('budget', 'error_rate', 'latency', 'quota');--> statement-breakpoint
+CREATE TABLE "alert_channels" (
+ "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "alert_channels_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
+ "name" varchar(100) NOT NULL,
+ "type" "alert_channel_type" NOT NULL,
+ "config" jsonb NOT NULL,
+ "enabled" boolean DEFAULT true NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "alert_history" (
+ "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "alert_history_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
+ "rule_id" integer NOT NULL,
+ "triggered_at" timestamp DEFAULT now() NOT NULL,
+ "resolved_at" timestamp,
+ "payload" jsonb NOT NULL,
+ "status" "alert_history_status" NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "alert_rules" (
+ "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "alert_rules_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
+ "name" varchar(100) NOT NULL,
+ "type" "alert_rule_type" NOT NULL,
+ "condition" jsonb NOT NULL,
+ "channel_ids" integer[] NOT NULL,
+ "cooldown_minutes" integer DEFAULT 60 NOT NULL,
+ "enabled" boolean DEFAULT true NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "updated_at" timestamp DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+ALTER TABLE "alert_history" ADD CONSTRAINT "alert_history_rule_id_alert_rules_id_fk" FOREIGN KEY ("rule_id") REFERENCES "public"."alert_rules"("id") ON DELETE no action ON UPDATE no action;
\ No newline at end of file
diff --git a/backend/drizzle/0014_opposite_dragon_man.sql b/backend/drizzle/0014_opposite_dragon_man.sql
new file mode 100644
index 0000000..18bf64d
--- /dev/null
+++ b/backend/drizzle/0014_opposite_dragon_man.sql
@@ -0,0 +1,6 @@
+ALTER TABLE "alert_channels" ADD COLUMN "grafana_uid" varchar(127);--> statement-breakpoint
+ALTER TABLE "alert_channels" ADD COLUMN "grafana_synced_at" timestamp;--> statement-breakpoint
+ALTER TABLE "alert_channels" ADD COLUMN "grafana_sync_error" varchar(500);--> statement-breakpoint
+ALTER TABLE "alert_rules" ADD COLUMN "grafana_uid" varchar(127);--> statement-breakpoint
+ALTER TABLE "alert_rules" ADD COLUMN "grafana_synced_at" timestamp;--> statement-breakpoint
+ALTER TABLE "alert_rules" ADD COLUMN "grafana_sync_error" varchar(500);
\ No newline at end of file
diff --git a/backend/drizzle/0015_green_kate_bishop.sql b/backend/drizzle/0015_green_kate_bishop.sql
new file mode 100644
index 0000000..3a46948
--- /dev/null
+++ b/backend/drizzle/0015_green_kate_bishop.sql
@@ -0,0 +1,3 @@
+ALTER TABLE "alert_history" DROP CONSTRAINT "alert_history_rule_id_alert_rules_id_fk";
+--> statement-breakpoint
+ALTER TABLE "alert_history" ADD CONSTRAINT "alert_history_rule_id_alert_rules_id_fk" FOREIGN KEY ("rule_id") REFERENCES "public"."alert_rules"("id") ON DELETE cascade ON UPDATE no action;
\ No newline at end of file
diff --git a/backend/drizzle/meta/0013_snapshot.json b/backend/drizzle/meta/0013_snapshot.json
new file mode 100644
index 0000000..076f16b
--- /dev/null
+++ b/backend/drizzle/meta/0013_snapshot.json
@@ -0,0 +1,1289 @@
+{
+ "id": "976baa6f-9ac9-403e-a0ca-e3a1dacab706",
+ "prevId": "108dfda0-d871-4758-b0c7-678298118347",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.alert_channels": {
+ "name": "alert_channels",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "alert_channels_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "alert_channel_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.alert_history": {
+ "name": "alert_history",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "alert_history_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "rule_id": {
+ "name": "rule_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "triggered_at": {
+ "name": "triggered_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "alert_history_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "alert_history_rule_id_alert_rules_id_fk": {
+ "name": "alert_history_rule_id_alert_rules_id_fk",
+ "tableFrom": "alert_history",
+ "tableTo": "alert_rules",
+ "columnsFrom": [
+ "rule_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.alert_rules": {
+ "name": "alert_rules",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "alert_rules_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "alert_rule_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition": {
+ "name": "condition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_ids": {
+ "name": "channel_ids",
+ "type": "integer[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cooldown_minutes": {
+ "name": "cooldown_minutes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.api_keys": {
+ "name": "api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "api_keys_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_seen": {
+ "name": "last_seen",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked": {
+ "name": "revoked",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "rpm_limit": {
+ "name": "rpm_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "tpm_limit": {
+ "name": "tpm_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50000
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "api_key_source",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'manual'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "api_keys_key_unique": {
+ "name": "api_keys_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ },
+ "api_keys_external_id_unique": {
+ "name": "api_keys_external_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "external_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.completions": {
+ "name": "completions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "completions_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "api_key_id": {
+ "name": "api_key_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "upstream_id": {
+ "name": "upstream_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model_id": {
+ "name": "model_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "prompt": {
+ "name": "prompt",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "prompt_tokens": {
+ "name": "prompt_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "completion": {
+ "name": "completion",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "completion_tokens": {
+ "name": "completion_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "completions_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "ttft": {
+ "name": "ttft",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "rating": {
+ "name": "rating",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "req_id": {
+ "name": "req_id",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_completion_id": {
+ "name": "source_completion_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_format": {
+ "name": "api_format",
+ "type": "varchar(31)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cached_response": {
+ "name": "cached_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "completions_api_key_id_api_keys_id_fk": {
+ "name": "completions_api_key_id_api_keys_id_fk",
+ "tableFrom": "completions",
+ "tableTo": "api_keys",
+ "columnsFrom": [
+ "api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "completions_upstream_id_upstreams_id_fk": {
+ "name": "completions_upstream_id_upstreams_id_fk",
+ "tableFrom": "completions",
+ "tableTo": "upstreams",
+ "columnsFrom": [
+ "upstream_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "completions_source_completion_id_completions_id_fk": {
+ "name": "completions_source_completion_id_completions_id_fk",
+ "tableFrom": "completions",
+ "tableTo": "completions",
+ "columnsFrom": [
+ "source_completion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "completions_id_unique": {
+ "name": "completions_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.embeddings": {
+ "name": "embeddings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "embeddings_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "api_key_id": {
+ "name": "api_key_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model_id": {
+ "name": "model_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input": {
+ "name": "input",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input_tokens": {
+ "name": "input_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "embedding": {
+ "name": "embedding",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dimensions": {
+ "name": "dimensions",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "completions_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "embeddings_api_key_id_api_keys_id_fk": {
+ "name": "embeddings_api_key_id_api_keys_id_fk",
+ "tableFrom": "embeddings",
+ "tableTo": "api_keys",
+ "columnsFrom": [
+ "api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "embeddings_model_id_models_id_fk": {
+ "name": "embeddings_model_id_models_id_fk",
+ "tableFrom": "embeddings",
+ "tableTo": "models",
+ "columnsFrom": [
+ "model_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "embeddings_id_unique": {
+ "name": "embeddings_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.models": {
+ "name": "models",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "models_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "system_name": {
+ "name": "system_name",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "remote_id": {
+ "name": "remote_id",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model_type": {
+ "name": "model_type",
+ "type": "model_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'chat'"
+ },
+ "context_length": {
+ "name": "context_length",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "input_price": {
+ "name": "input_price",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price": {
+ "name": "output_price",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "weight": {
+ "name": "weight",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "models_provider_id_providers_id_fk": {
+ "name": "models_provider_id_providers_id_fk",
+ "tableFrom": "models",
+ "tableTo": "providers",
+ "columnsFrom": [
+ "provider_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "models_provider_system_name_unique": {
+ "name": "models_provider_system_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id",
+ "system_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.providers": {
+ "name": "providers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "providers_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "provider_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai'"
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "api_key": {
+ "name": "api_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_version": {
+ "name": "api_version",
+ "type": "varchar(31)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.settings": {
+ "name": "settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "settings_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "settings_key_unique": {
+ "name": "settings_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.srv_logs": {
+ "name": "srv_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "srv_logs_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "related_api_key_id": {
+ "name": "related_api_key_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_upstream_id": {
+ "name": "related_upstream_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_completion_id": {
+ "name": "related_completion_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message": {
+ "name": "message",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level": {
+ "name": "level",
+ "type": "srv_logs_level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "details": {
+ "name": "details",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "acknowledged": {
+ "name": "acknowledged",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "ack_at": {
+ "name": "ack_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "srv_logs_related_api_key_id_api_keys_id_fk": {
+ "name": "srv_logs_related_api_key_id_api_keys_id_fk",
+ "tableFrom": "srv_logs",
+ "tableTo": "api_keys",
+ "columnsFrom": [
+ "related_api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "srv_logs_related_upstream_id_upstreams_id_fk": {
+ "name": "srv_logs_related_upstream_id_upstreams_id_fk",
+ "tableFrom": "srv_logs",
+ "tableTo": "upstreams",
+ "columnsFrom": [
+ "related_upstream_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "srv_logs_related_completion_id_completions_id_fk": {
+ "name": "srv_logs_related_completion_id_completions_id_fk",
+ "tableFrom": "srv_logs",
+ "tableTo": "completions",
+ "columnsFrom": [
+ "related_completion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "srv_logs_id_unique": {
+ "name": "srv_logs_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.upstreams": {
+ "name": "upstreams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "upstreams_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "upstream_model": {
+ "name": "upstream_model",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_key": {
+ "name": "api_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "weight": {
+ "name": "weight",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.alert_channel_type": {
+ "name": "alert_channel_type",
+ "schema": "public",
+ "values": [
+ "webhook",
+ "email",
+ "feishu"
+ ]
+ },
+ "public.alert_history_status": {
+ "name": "alert_history_status",
+ "schema": "public",
+ "values": [
+ "sent",
+ "failed",
+ "suppressed"
+ ]
+ },
+ "public.alert_rule_type": {
+ "name": "alert_rule_type",
+ "schema": "public",
+ "values": [
+ "budget",
+ "error_rate",
+ "latency",
+ "quota"
+ ]
+ },
+ "public.api_key_source": {
+ "name": "api_key_source",
+ "schema": "public",
+ "values": [
+ "manual",
+ "operator",
+ "init"
+ ]
+ },
+ "public.completions_status": {
+ "name": "completions_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "completed",
+ "failed",
+ "aborted",
+ "cache_hit"
+ ]
+ },
+ "public.model_type": {
+ "name": "model_type",
+ "schema": "public",
+ "values": [
+ "chat",
+ "embedding"
+ ]
+ },
+ "public.provider_type": {
+ "name": "provider_type",
+ "schema": "public",
+ "values": [
+ "openai",
+ "openai-responses",
+ "anthropic",
+ "azure",
+ "ollama"
+ ]
+ },
+ "public.srv_logs_level": {
+ "name": "srv_logs_level",
+ "schema": "public",
+ "values": [
+ "unspecific",
+ "info",
+ "warn",
+ "error"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/backend/drizzle/meta/0014_snapshot.json b/backend/drizzle/meta/0014_snapshot.json
new file mode 100644
index 0000000..15269fe
--- /dev/null
+++ b/backend/drizzle/meta/0014_snapshot.json
@@ -0,0 +1,1325 @@
+{
+ "id": "5baa8f77-48e3-4e1d-9078-d979c2312412",
+ "prevId": "976baa6f-9ac9-403e-a0ca-e3a1dacab706",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.alert_channels": {
+ "name": "alert_channels",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "alert_channels_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "alert_channel_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "grafana_uid": {
+ "name": "grafana_uid",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grafana_synced_at": {
+ "name": "grafana_synced_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grafana_sync_error": {
+ "name": "grafana_sync_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.alert_history": {
+ "name": "alert_history",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "alert_history_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "rule_id": {
+ "name": "rule_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "triggered_at": {
+ "name": "triggered_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "alert_history_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "alert_history_rule_id_alert_rules_id_fk": {
+ "name": "alert_history_rule_id_alert_rules_id_fk",
+ "tableFrom": "alert_history",
+ "tableTo": "alert_rules",
+ "columnsFrom": [
+ "rule_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.alert_rules": {
+ "name": "alert_rules",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "alert_rules_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "alert_rule_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition": {
+ "name": "condition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_ids": {
+ "name": "channel_ids",
+ "type": "integer[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cooldown_minutes": {
+ "name": "cooldown_minutes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "grafana_uid": {
+ "name": "grafana_uid",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grafana_synced_at": {
+ "name": "grafana_synced_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grafana_sync_error": {
+ "name": "grafana_sync_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.api_keys": {
+ "name": "api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "api_keys_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_seen": {
+ "name": "last_seen",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked": {
+ "name": "revoked",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "rpm_limit": {
+ "name": "rpm_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "tpm_limit": {
+ "name": "tpm_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50000
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "api_key_source",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'manual'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "api_keys_key_unique": {
+ "name": "api_keys_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ },
+ "api_keys_external_id_unique": {
+ "name": "api_keys_external_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "external_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.completions": {
+ "name": "completions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "completions_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "api_key_id": {
+ "name": "api_key_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "upstream_id": {
+ "name": "upstream_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model_id": {
+ "name": "model_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "prompt": {
+ "name": "prompt",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "prompt_tokens": {
+ "name": "prompt_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "completion": {
+ "name": "completion",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "completion_tokens": {
+ "name": "completion_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "completions_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "ttft": {
+ "name": "ttft",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "rating": {
+ "name": "rating",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "req_id": {
+ "name": "req_id",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_completion_id": {
+ "name": "source_completion_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_format": {
+ "name": "api_format",
+ "type": "varchar(31)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cached_response": {
+ "name": "cached_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "completions_api_key_id_api_keys_id_fk": {
+ "name": "completions_api_key_id_api_keys_id_fk",
+ "tableFrom": "completions",
+ "tableTo": "api_keys",
+ "columnsFrom": [
+ "api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "completions_upstream_id_upstreams_id_fk": {
+ "name": "completions_upstream_id_upstreams_id_fk",
+ "tableFrom": "completions",
+ "tableTo": "upstreams",
+ "columnsFrom": [
+ "upstream_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "completions_source_completion_id_completions_id_fk": {
+ "name": "completions_source_completion_id_completions_id_fk",
+ "tableFrom": "completions",
+ "tableTo": "completions",
+ "columnsFrom": [
+ "source_completion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "completions_id_unique": {
+ "name": "completions_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.embeddings": {
+ "name": "embeddings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "embeddings_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "api_key_id": {
+ "name": "api_key_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model_id": {
+ "name": "model_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input": {
+ "name": "input",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input_tokens": {
+ "name": "input_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "embedding": {
+ "name": "embedding",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dimensions": {
+ "name": "dimensions",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "completions_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "embeddings_api_key_id_api_keys_id_fk": {
+ "name": "embeddings_api_key_id_api_keys_id_fk",
+ "tableFrom": "embeddings",
+ "tableTo": "api_keys",
+ "columnsFrom": [
+ "api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "embeddings_model_id_models_id_fk": {
+ "name": "embeddings_model_id_models_id_fk",
+ "tableFrom": "embeddings",
+ "tableTo": "models",
+ "columnsFrom": [
+ "model_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "embeddings_id_unique": {
+ "name": "embeddings_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.models": {
+ "name": "models",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "models_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "system_name": {
+ "name": "system_name",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "remote_id": {
+ "name": "remote_id",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model_type": {
+ "name": "model_type",
+ "type": "model_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'chat'"
+ },
+ "context_length": {
+ "name": "context_length",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "input_price": {
+ "name": "input_price",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price": {
+ "name": "output_price",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "weight": {
+ "name": "weight",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "models_provider_id_providers_id_fk": {
+ "name": "models_provider_id_providers_id_fk",
+ "tableFrom": "models",
+ "tableTo": "providers",
+ "columnsFrom": [
+ "provider_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "models_provider_system_name_unique": {
+ "name": "models_provider_system_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id",
+ "system_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.providers": {
+ "name": "providers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "providers_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "provider_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai'"
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "api_key": {
+ "name": "api_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_version": {
+ "name": "api_version",
+ "type": "varchar(31)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.settings": {
+ "name": "settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "settings_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "settings_key_unique": {
+ "name": "settings_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.srv_logs": {
+ "name": "srv_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "srv_logs_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "related_api_key_id": {
+ "name": "related_api_key_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_upstream_id": {
+ "name": "related_upstream_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_completion_id": {
+ "name": "related_completion_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message": {
+ "name": "message",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level": {
+ "name": "level",
+ "type": "srv_logs_level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "details": {
+ "name": "details",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "acknowledged": {
+ "name": "acknowledged",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "ack_at": {
+ "name": "ack_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "srv_logs_related_api_key_id_api_keys_id_fk": {
+ "name": "srv_logs_related_api_key_id_api_keys_id_fk",
+ "tableFrom": "srv_logs",
+ "tableTo": "api_keys",
+ "columnsFrom": [
+ "related_api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "srv_logs_related_upstream_id_upstreams_id_fk": {
+ "name": "srv_logs_related_upstream_id_upstreams_id_fk",
+ "tableFrom": "srv_logs",
+ "tableTo": "upstreams",
+ "columnsFrom": [
+ "related_upstream_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "srv_logs_related_completion_id_completions_id_fk": {
+ "name": "srv_logs_related_completion_id_completions_id_fk",
+ "tableFrom": "srv_logs",
+ "tableTo": "completions",
+ "columnsFrom": [
+ "related_completion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "srv_logs_id_unique": {
+ "name": "srv_logs_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.upstreams": {
+ "name": "upstreams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "upstreams_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "upstream_model": {
+ "name": "upstream_model",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_key": {
+ "name": "api_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "weight": {
+ "name": "weight",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.alert_channel_type": {
+ "name": "alert_channel_type",
+ "schema": "public",
+ "values": [
+ "webhook",
+ "email",
+ "feishu"
+ ]
+ },
+ "public.alert_history_status": {
+ "name": "alert_history_status",
+ "schema": "public",
+ "values": [
+ "sent",
+ "failed",
+ "suppressed"
+ ]
+ },
+ "public.alert_rule_type": {
+ "name": "alert_rule_type",
+ "schema": "public",
+ "values": [
+ "budget",
+ "error_rate",
+ "latency",
+ "quota"
+ ]
+ },
+ "public.api_key_source": {
+ "name": "api_key_source",
+ "schema": "public",
+ "values": [
+ "manual",
+ "operator",
+ "init"
+ ]
+ },
+ "public.completions_status": {
+ "name": "completions_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "completed",
+ "failed",
+ "aborted",
+ "cache_hit"
+ ]
+ },
+ "public.model_type": {
+ "name": "model_type",
+ "schema": "public",
+ "values": [
+ "chat",
+ "embedding"
+ ]
+ },
+ "public.provider_type": {
+ "name": "provider_type",
+ "schema": "public",
+ "values": [
+ "openai",
+ "openai-responses",
+ "anthropic",
+ "azure",
+ "ollama"
+ ]
+ },
+ "public.srv_logs_level": {
+ "name": "srv_logs_level",
+ "schema": "public",
+ "values": [
+ "unspecific",
+ "info",
+ "warn",
+ "error"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/backend/drizzle/meta/0015_snapshot.json b/backend/drizzle/meta/0015_snapshot.json
new file mode 100644
index 0000000..0186b8e
--- /dev/null
+++ b/backend/drizzle/meta/0015_snapshot.json
@@ -0,0 +1,1325 @@
+{
+ "id": "fa45eb3e-7bca-4be9-9142-78bf48f320e2",
+ "prevId": "5baa8f77-48e3-4e1d-9078-d979c2312412",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.alert_channels": {
+ "name": "alert_channels",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "alert_channels_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "alert_channel_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "grafana_uid": {
+ "name": "grafana_uid",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grafana_synced_at": {
+ "name": "grafana_synced_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grafana_sync_error": {
+ "name": "grafana_sync_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.alert_history": {
+ "name": "alert_history",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "alert_history_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "rule_id": {
+ "name": "rule_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "triggered_at": {
+ "name": "triggered_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "alert_history_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "alert_history_rule_id_alert_rules_id_fk": {
+ "name": "alert_history_rule_id_alert_rules_id_fk",
+ "tableFrom": "alert_history",
+ "tableTo": "alert_rules",
+ "columnsFrom": [
+ "rule_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.alert_rules": {
+ "name": "alert_rules",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "alert_rules_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "alert_rule_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition": {
+ "name": "condition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel_ids": {
+ "name": "channel_ids",
+ "type": "integer[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "cooldown_minutes": {
+ "name": "cooldown_minutes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 60
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "grafana_uid": {
+ "name": "grafana_uid",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grafana_synced_at": {
+ "name": "grafana_synced_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "grafana_sync_error": {
+ "name": "grafana_sync_error",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.api_keys": {
+ "name": "api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "api_keys_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_seen": {
+ "name": "last_seen",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "revoked": {
+ "name": "revoked",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "rpm_limit": {
+ "name": "rpm_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50
+ },
+ "tpm_limit": {
+ "name": "tpm_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 50000
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source": {
+ "name": "source",
+ "type": "api_key_source",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'manual'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "api_keys_key_unique": {
+ "name": "api_keys_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ },
+ "api_keys_external_id_unique": {
+ "name": "api_keys_external_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "external_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.completions": {
+ "name": "completions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "completions_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "api_key_id": {
+ "name": "api_key_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "upstream_id": {
+ "name": "upstream_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model_id": {
+ "name": "model_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "prompt": {
+ "name": "prompt",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "prompt_tokens": {
+ "name": "prompt_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "completion": {
+ "name": "completion",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "completion_tokens": {
+ "name": "completion_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "completions_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "ttft": {
+ "name": "ttft",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "rating": {
+ "name": "rating",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "req_id": {
+ "name": "req_id",
+ "type": "varchar(127)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "source_completion_id": {
+ "name": "source_completion_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_format": {
+ "name": "api_format",
+ "type": "varchar(31)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "cached_response": {
+ "name": "cached_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "completions_api_key_id_api_keys_id_fk": {
+ "name": "completions_api_key_id_api_keys_id_fk",
+ "tableFrom": "completions",
+ "tableTo": "api_keys",
+ "columnsFrom": [
+ "api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "completions_upstream_id_upstreams_id_fk": {
+ "name": "completions_upstream_id_upstreams_id_fk",
+ "tableFrom": "completions",
+ "tableTo": "upstreams",
+ "columnsFrom": [
+ "upstream_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "completions_source_completion_id_completions_id_fk": {
+ "name": "completions_source_completion_id_completions_id_fk",
+ "tableFrom": "completions",
+ "tableTo": "completions",
+ "columnsFrom": [
+ "source_completion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "completions_id_unique": {
+ "name": "completions_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.embeddings": {
+ "name": "embeddings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "embeddings_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "api_key_id": {
+ "name": "api_key_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model_id": {
+ "name": "model_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model": {
+ "name": "model",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input": {
+ "name": "input",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "input_tokens": {
+ "name": "input_tokens",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "embedding": {
+ "name": "embedding",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dimensions": {
+ "name": "dimensions",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "completions_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "duration": {
+ "name": "duration",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "embeddings_api_key_id_api_keys_id_fk": {
+ "name": "embeddings_api_key_id_api_keys_id_fk",
+ "tableFrom": "embeddings",
+ "tableTo": "api_keys",
+ "columnsFrom": [
+ "api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "embeddings_model_id_models_id_fk": {
+ "name": "embeddings_model_id_models_id_fk",
+ "tableFrom": "embeddings",
+ "tableTo": "models",
+ "columnsFrom": [
+ "model_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "embeddings_id_unique": {
+ "name": "embeddings_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.models": {
+ "name": "models",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "models_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "system_name": {
+ "name": "system_name",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "remote_id": {
+ "name": "remote_id",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "model_type": {
+ "name": "model_type",
+ "type": "model_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'chat'"
+ },
+ "context_length": {
+ "name": "context_length",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "input_price": {
+ "name": "input_price",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output_price": {
+ "name": "output_price",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "weight": {
+ "name": "weight",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "models_provider_id_providers_id_fk": {
+ "name": "models_provider_id_providers_id_fk",
+ "tableFrom": "models",
+ "tableTo": "providers",
+ "columnsFrom": [
+ "provider_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "models_provider_system_name_unique": {
+ "name": "models_provider_system_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id",
+ "system_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.providers": {
+ "name": "providers",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "providers_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "provider_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'openai'"
+ },
+ "base_url": {
+ "name": "base_url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "api_key": {
+ "name": "api_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_version": {
+ "name": "api_version",
+ "type": "varchar(31)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.settings": {
+ "name": "settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "settings_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "settings_key_unique": {
+ "name": "settings_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.srv_logs": {
+ "name": "srv_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "srv_logs_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "related_api_key_id": {
+ "name": "related_api_key_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_upstream_id": {
+ "name": "related_upstream_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "related_completion_id": {
+ "name": "related_completion_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message": {
+ "name": "message",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level": {
+ "name": "level",
+ "type": "srv_logs_level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "details": {
+ "name": "details",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "acknowledged": {
+ "name": "acknowledged",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "ack_at": {
+ "name": "ack_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "srv_logs_related_api_key_id_api_keys_id_fk": {
+ "name": "srv_logs_related_api_key_id_api_keys_id_fk",
+ "tableFrom": "srv_logs",
+ "tableTo": "api_keys",
+ "columnsFrom": [
+ "related_api_key_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "srv_logs_related_upstream_id_upstreams_id_fk": {
+ "name": "srv_logs_related_upstream_id_upstreams_id_fk",
+ "tableFrom": "srv_logs",
+ "tableTo": "upstreams",
+ "columnsFrom": [
+ "related_upstream_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "srv_logs_related_completion_id_completions_id_fk": {
+ "name": "srv_logs_related_completion_id_completions_id_fk",
+ "tableFrom": "srv_logs",
+ "tableTo": "completions",
+ "columnsFrom": [
+ "related_completion_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "srv_logs_id_unique": {
+ "name": "srv_logs_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.upstreams": {
+ "name": "upstreams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "upstreams_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "url": {
+ "name": "url",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "model": {
+ "name": "model",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "upstream_model": {
+ "name": "upstream_model",
+ "type": "varchar(63)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "api_key": {
+ "name": "api_key",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "weight": {
+ "name": "weight",
+ "type": "real",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "comment": {
+ "name": "comment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted": {
+ "name": "deleted",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.alert_channel_type": {
+ "name": "alert_channel_type",
+ "schema": "public",
+ "values": [
+ "webhook",
+ "email",
+ "feishu"
+ ]
+ },
+ "public.alert_history_status": {
+ "name": "alert_history_status",
+ "schema": "public",
+ "values": [
+ "sent",
+ "failed",
+ "suppressed"
+ ]
+ },
+ "public.alert_rule_type": {
+ "name": "alert_rule_type",
+ "schema": "public",
+ "values": [
+ "budget",
+ "error_rate",
+ "latency",
+ "quota"
+ ]
+ },
+ "public.api_key_source": {
+ "name": "api_key_source",
+ "schema": "public",
+ "values": [
+ "manual",
+ "operator",
+ "init"
+ ]
+ },
+ "public.completions_status": {
+ "name": "completions_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "completed",
+ "failed",
+ "aborted",
+ "cache_hit"
+ ]
+ },
+ "public.model_type": {
+ "name": "model_type",
+ "schema": "public",
+ "values": [
+ "chat",
+ "embedding"
+ ]
+ },
+ "public.provider_type": {
+ "name": "provider_type",
+ "schema": "public",
+ "values": [
+ "openai",
+ "openai-responses",
+ "anthropic",
+ "azure",
+ "ollama"
+ ]
+ },
+ "public.srv_logs_level": {
+ "name": "srv_logs_level",
+ "schema": "public",
+ "values": [
+ "unspecific",
+ "info",
+ "warn",
+ "error"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/backend/drizzle/meta/_journal.json b/backend/drizzle/meta/_journal.json
index c5728e8..8c1ce13 100644
--- a/backend/drizzle/meta/_journal.json
+++ b/backend/drizzle/meta/_journal.json
@@ -92,6 +92,27 @@
"when": 1769694986161,
"tag": "0012_right_iceman",
"breakpoints": true
+ },
+ {
+ "idx": 13,
+ "version": "7",
+ "when": 1769859175301,
+ "tag": "0013_flowery_maria_hill",
+ "breakpoints": true
+ },
+ {
+ "idx": 14,
+ "version": "7",
+ "when": 1769870349819,
+ "tag": "0014_opposite_dragon_man",
+ "breakpoints": true
+ },
+ {
+ "idx": 15,
+ "version": "7",
+ "when": 1769874323194,
+ "tag": "0015_green_kate_bishop",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/backend/package.json b/backend/package.json
index 6fccf9c..1b87694 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -25,11 +25,13 @@
"elysia": "^1.4.22",
"ioredis": "^5.9.1",
"loglayer": "^8.4.0",
+ "nodemailer": "^7.0.13",
"zod": "^3.25.76"
},
"devDependencies": {
"@types/bun": "^1.3.6",
"@types/node": "^24.10.9",
+ "@types/nodemailer": "^7.0.9",
"@typescript/native-preview": "7.0.0-dev.20260124.1",
"drizzle-kit": "^0.31.8",
"openai": "^6.16.0",
diff --git a/backend/src/adapters/upstream/anthropic.ts b/backend/src/adapters/upstream/anthropic.ts
index c236592..5c4f61d 100644
--- a/backend/src/adapters/upstream/anthropic.ts
+++ b/backend/src/adapters/upstream/anthropic.ts
@@ -461,8 +461,7 @@ export const anthropicUpstreamAdapter: UpstreamAdapter = {
...(request.stopSequences && { stop_sequences: request.stopSequences }),
...(request.tools && { tools: convertTools(request.tools) }),
...(request.toolChoice && {
- tool_choice: convertToolChoice(request.toolChoice),
- }),
+ tool_choice: convertToolChoice(request.toolChoice), }),
...request.extraParams,
};
diff --git a/backend/src/api/admin/alerts.ts b/backend/src/api/admin/alerts.ts
new file mode 100644
index 0000000..09731fe
--- /dev/null
+++ b/backend/src/api/admin/alerts.ts
@@ -0,0 +1,310 @@
+import { Elysia, t } from "elysia";
+import {
+ deleteAlertChannel,
+ deleteAlertRule,
+ findAlertChannel,
+ findAlertRule,
+ insertAlertChannel,
+ insertAlertRule,
+ listAlertChannels,
+ listAlertHistory,
+ listAlertRules,
+ updateAlertChannel,
+ updateAlertRule,
+} from "@/db";
+import { sendTestNotification } from "@/services/alertDispatcher";
+import type {
+ AlertChannelConfig,
+ AlertCondition,
+} from "@/db/schema";
+
+export const adminAlerts = new Elysia({ prefix: "/alerts" })
+ // ============================================
+ // Alert Channels
+ // ============================================
+ .get(
+ "/channels",
+ async () => {
+ return await listAlertChannels();
+ },
+ {
+ detail: {
+ description: "List all alert channels",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ .get(
+ "/channels/:id",
+ async ({ params: { id }, status }) => {
+ const channel = await findAlertChannel(id);
+ if (!channel) {
+ return status(404, { error: "Alert channel not found" });
+ }
+ return channel;
+ },
+ {
+ params: t.Object({ id: t.Numeric() }),
+ detail: {
+ description: "Get an alert channel by ID",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ .post(
+ "/channels",
+ async ({ body }) => {
+ const channel = await insertAlertChannel({
+ name: body.name,
+ type: body.type,
+ config: body.config as AlertChannelConfig,
+ enabled: body.enabled,
+ });
+ return channel;
+ },
+ {
+ body: t.Object({
+ name: t.String({ minLength: 1, maxLength: 100 }),
+ type: t.Union([
+ t.Literal("webhook"),
+ t.Literal("email"),
+ t.Literal("feishu"),
+ ]),
+ config: t.Unknown(),
+ enabled: t.Optional(t.Boolean()),
+ }),
+ detail: {
+ description: "Create a new alert channel",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ .put(
+ "/channels/:id",
+ async ({ params: { id }, body, status }) => {
+ const existing = await findAlertChannel(id);
+ if (!existing) {
+ return status(404, { error: "Alert channel not found" });
+ }
+ const channel = await updateAlertChannel(id, {
+ name: body.name,
+ type: body.type,
+ config: body.config as AlertChannelConfig | undefined,
+ enabled: body.enabled,
+ });
+ return channel;
+ },
+ {
+ params: t.Object({ id: t.Numeric() }),
+ body: t.Object({
+ name: t.Optional(t.String({ minLength: 1, maxLength: 100 })),
+ type: t.Optional(
+ t.Union([
+ t.Literal("webhook"),
+ t.Literal("email"),
+ t.Literal("feishu"),
+ ]),
+ ),
+ config: t.Optional(t.Unknown()),
+ enabled: t.Optional(t.Boolean()),
+ }),
+ detail: {
+ description: "Update an alert channel",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ .delete(
+ "/channels/:id",
+ async ({ params: { id }, status }) => {
+ const channel = await deleteAlertChannel(id);
+ if (!channel) {
+ return status(404, { error: "Alert channel not found" });
+ }
+ return { success: true };
+ },
+ {
+ params: t.Object({ id: t.Numeric() }),
+ detail: {
+ description: "Delete an alert channel",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ .post(
+ "/channels/:id/test",
+ async ({ params: { id }, status }) => {
+ const channel = await findAlertChannel(id);
+ if (!channel) {
+ return status(404, { error: "Alert channel not found" });
+ }
+
+ try {
+ await sendTestNotification(channel.type, channel.config);
+ return { success: true, message: "Test notification sent" };
+ } catch (e) {
+ return status(502, {
+ success: false,
+ error: e instanceof Error ? e.message : "Unknown error",
+ });
+ }
+ },
+ {
+ params: t.Object({ id: t.Numeric() }),
+ detail: {
+ description: "Send a test notification to an alert channel",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ // ============================================
+ // Alert Rules
+ // ============================================
+ .get(
+ "/rules",
+ async () => {
+ return await listAlertRules();
+ },
+ {
+ detail: {
+ description: "List all alert rules",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ .get(
+ "/rules/:id",
+ async ({ params: { id }, status }) => {
+ const rule = await findAlertRule(id);
+ if (!rule) {
+ return status(404, { error: "Alert rule not found" });
+ }
+ return { ...rule, channelIds: [...rule.channelIds] };
+ },
+ {
+ params: t.Object({ id: t.Numeric() }),
+ detail: {
+ description: "Get an alert rule by ID",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ .post(
+ "/rules",
+ async ({ body }) => {
+ const rule = await insertAlertRule({
+ name: body.name,
+ type: body.type,
+ condition: body.condition as AlertCondition,
+ channelIds: body.channelIds,
+ cooldownMinutes: body.cooldownMinutes,
+ enabled: body.enabled,
+ });
+ if (!rule) {
+ return rule;
+ }
+ return { ...rule, channelIds: [...rule.channelIds] };
+ },
+ {
+ body: t.Object({
+ name: t.String({ minLength: 1, maxLength: 100 }),
+ type: t.Union([
+ t.Literal("budget"),
+ t.Literal("error_rate"),
+ t.Literal("latency"),
+ t.Literal("quota"),
+ ]),
+ condition: t.Unknown(),
+ channelIds: t.Array(t.Number()),
+ cooldownMinutes: t.Optional(t.Number({ minimum: 1 })),
+ enabled: t.Optional(t.Boolean()),
+ }),
+ detail: {
+ description: "Create a new alert rule",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ .put(
+ "/rules/:id",
+ async ({ params: { id }, body, status }) => {
+ const existing = await findAlertRule(id);
+ if (!existing) {
+ return status(404, { error: "Alert rule not found" });
+ }
+ const rule = await updateAlertRule(id, {
+ name: body.name,
+ type: body.type,
+ condition: body.condition as AlertCondition | undefined,
+ channelIds: body.channelIds,
+ cooldownMinutes: body.cooldownMinutes,
+ enabled: body.enabled,
+ });
+ if (!rule) {
+ return rule;
+ }
+ return { ...rule, channelIds: [...rule.channelIds] };
+ },
+ {
+ params: t.Object({ id: t.Numeric() }),
+ body: t.Object({
+ name: t.Optional(t.String({ minLength: 1, maxLength: 100 })),
+ type: t.Optional(
+ t.Union([
+ t.Literal("budget"),
+ t.Literal("error_rate"),
+ t.Literal("latency"),
+ t.Literal("quota"),
+ ]),
+ ),
+ condition: t.Optional(t.Unknown()),
+ channelIds: t.Optional(t.Array(t.Number())),
+ cooldownMinutes: t.Optional(t.Number({ minimum: 1 })),
+ enabled: t.Optional(t.Boolean()),
+ }),
+ detail: {
+ description: "Update an alert rule",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ .delete(
+ "/rules/:id",
+ async ({ params: { id }, status }) => {
+ const rule = await deleteAlertRule(id);
+ if (!rule) {
+ return status(404, { error: "Alert rule not found" });
+ }
+ return { success: true };
+ },
+ {
+ params: t.Object({ id: t.Numeric() }),
+ detail: {
+ description: "Delete an alert rule",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ )
+ // ============================================
+ // Alert History
+ // ============================================
+ .get(
+ "/history",
+ async ({ query }) => {
+ const offset = query.offset ?? 0;
+ const limit = query.limit ?? 50;
+ const ruleId = query.ruleId;
+ return await listAlertHistory(offset, limit, ruleId);
+ },
+ {
+ query: t.Object({
+ offset: t.Optional(t.Numeric()),
+ limit: t.Optional(t.Numeric()),
+ ruleId: t.Optional(t.Numeric()),
+ }),
+ detail: {
+ description: "List alert history (paginated, optionally filtered by rule)",
+ tags: ["Admin - Alerts"],
+ },
+ },
+ );
diff --git a/backend/src/api/admin/grafana.ts b/backend/src/api/admin/grafana.ts
new file mode 100644
index 0000000..8e6463b
--- /dev/null
+++ b/backend/src/api/admin/grafana.ts
@@ -0,0 +1,303 @@
+import { Elysia, t } from "elysia";
+import {
+ getSetting,
+ upsertSetting,
+ deleteSetting,
+ listAlertRules,
+ listAlertChannels,
+} from "@/db";
+import { createLogger } from "@/utils/logger";
+import {
+ syncRulesToGrafana,
+ syncChannelsToGrafana,
+ syncAllToGrafana,
+} from "@/services/grafanaSync";
+
+const logger = createLogger("adminGrafana");
+
+const GRAFANA_CONNECTION_KEY = "grafana_connection";
+
+interface GrafanaConnection {
+ apiUrl: string;
+ authToken: string;
+ datasourceUid?: string;
+ verified: boolean;
+ verifiedAt: string | null;
+}
+
+async function getGrafanaConnection(): Promise Rule: ${escapeHtml(payload.ruleName)} Type: ${escapeHtml(payload.ruleType)} Message: ${escapeHtml(payload.message)} Current Value: ${escapeHtml(String(payload.currentValue))} Threshold: ${escapeHtml(String(payload.threshold))} Details: NexusGate Alert
+ ${escapeHtml(JSON.stringify(payload.details, null, 2))}
{t('pages.settings.grafana.Description')}
+{connection.datasourceUid}
+
+ )}
+ {connection.verifiedAt && (
+
+ {t('pages.settings.grafana.VerifiedAt')}: {formatDistanceToNow(new Date(connection.verifiedAt), { addSuffix: true })}
+
+ )}
+