diff --git a/backend/drizzle/0017_superb_sentinels.sql b/backend/drizzle/0017_superb_sentinels.sql new file mode 100644 index 0000000..359603b --- /dev/null +++ b/backend/drizzle/0017_superb_sentinels.sql @@ -0,0 +1,65 @@ +CREATE TYPE "public"."playground_message_role" AS ENUM('system', 'user', 'assistant');--> statement-breakpoint +CREATE TYPE "public"."playground_test_result_status" AS ENUM('pending', 'running', 'completed', 'failed');--> statement-breakpoint +CREATE TABLE "playground_conversations" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "playground_conversations_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "title" varchar(255) NOT NULL, + "model" varchar(63) NOT NULL, + "api_key_id" integer, + "params" jsonb, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "deleted" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +CREATE TABLE "playground_messages" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "playground_messages_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "conversation_id" integer NOT NULL, + "role" "playground_message_role" NOT NULL, + "content" varchar NOT NULL, + "completion_id" integer, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "playground_test_cases" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "playground_test_cases_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "title" varchar(255) NOT NULL, + "description" varchar, + "messages" jsonb NOT NULL, + "params" jsonb, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL, + "deleted" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +CREATE TABLE "playground_test_results" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "playground_test_results_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "test_run_id" integer NOT NULL, + "model" varchar(63) NOT NULL, + "status" "playground_test_result_status" DEFAULT 'pending' NOT NULL, + "response" varchar, + "prompt_tokens" integer, + "completion_tokens" integer, + "ttft" integer, + "duration" integer, + "error_message" varchar, + "completion_id" integer, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "playground_test_runs" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "playground_test_runs_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "test_case_id" integer NOT NULL, + "api_key_id" integer, + "models" jsonb NOT NULL, + "created_at" timestamp DEFAULT now() NOT NULL, + "deleted" boolean DEFAULT false NOT NULL +); +--> statement-breakpoint +ALTER TABLE "playground_conversations" ADD CONSTRAINT "playground_conversations_api_key_id_api_keys_id_fk" FOREIGN KEY ("api_key_id") REFERENCES "public"."api_keys"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_messages" ADD CONSTRAINT "playground_messages_conversation_id_playground_conversations_id_fk" FOREIGN KEY ("conversation_id") REFERENCES "public"."playground_conversations"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_messages" ADD CONSTRAINT "playground_messages_completion_id_completions_id_fk" FOREIGN KEY ("completion_id") REFERENCES "public"."completions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_test_results" ADD CONSTRAINT "playground_test_results_test_run_id_playground_test_runs_id_fk" FOREIGN KEY ("test_run_id") REFERENCES "public"."playground_test_runs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_test_results" ADD CONSTRAINT "playground_test_results_completion_id_completions_id_fk" FOREIGN KEY ("completion_id") REFERENCES "public"."completions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_test_runs" ADD CONSTRAINT "playground_test_runs_test_case_id_playground_test_cases_id_fk" FOREIGN KEY ("test_case_id") REFERENCES "public"."playground_test_cases"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_test_runs" ADD CONSTRAINT "playground_test_runs_api_key_id_api_keys_id_fk" FOREIGN KEY ("api_key_id") REFERENCES "public"."api_keys"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/backend/drizzle/0018_messy_spectrum.sql b/backend/drizzle/0018_messy_spectrum.sql new file mode 100644 index 0000000..eb0dfdb --- /dev/null +++ b/backend/drizzle/0018_messy_spectrum.sql @@ -0,0 +1,21 @@ +ALTER TABLE "playground_conversations" DROP CONSTRAINT "playground_conversations_api_key_id_api_keys_id_fk"; +--> statement-breakpoint +ALTER TABLE "playground_messages" DROP CONSTRAINT "playground_messages_conversation_id_playground_conversations_id_fk"; +--> statement-breakpoint +ALTER TABLE "playground_messages" DROP CONSTRAINT "playground_messages_completion_id_completions_id_fk"; +--> statement-breakpoint +ALTER TABLE "playground_test_results" DROP CONSTRAINT "playground_test_results_test_run_id_playground_test_runs_id_fk"; +--> statement-breakpoint +ALTER TABLE "playground_test_results" DROP CONSTRAINT "playground_test_results_completion_id_completions_id_fk"; +--> statement-breakpoint +ALTER TABLE "playground_test_runs" DROP CONSTRAINT "playground_test_runs_test_case_id_playground_test_cases_id_fk"; +--> statement-breakpoint +ALTER TABLE "playground_test_runs" DROP CONSTRAINT "playground_test_runs_api_key_id_api_keys_id_fk"; +--> statement-breakpoint +ALTER TABLE "playground_conversations" ADD CONSTRAINT "playground_conversations_api_key_id_api_keys_id_fk" FOREIGN KEY ("api_key_id") REFERENCES "public"."api_keys"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_messages" ADD CONSTRAINT "playground_messages_conversation_id_playground_conversations_id_fk" FOREIGN KEY ("conversation_id") REFERENCES "public"."playground_conversations"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_messages" ADD CONSTRAINT "playground_messages_completion_id_completions_id_fk" FOREIGN KEY ("completion_id") REFERENCES "public"."completions"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_test_results" ADD CONSTRAINT "playground_test_results_test_run_id_playground_test_runs_id_fk" FOREIGN KEY ("test_run_id") REFERENCES "public"."playground_test_runs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_test_results" ADD CONSTRAINT "playground_test_results_completion_id_completions_id_fk" FOREIGN KEY ("completion_id") REFERENCES "public"."completions"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_test_runs" ADD CONSTRAINT "playground_test_runs_test_case_id_playground_test_cases_id_fk" FOREIGN KEY ("test_case_id") REFERENCES "public"."playground_test_cases"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "playground_test_runs" ADD CONSTRAINT "playground_test_runs_api_key_id_api_keys_id_fk" FOREIGN KEY ("api_key_id") REFERENCES "public"."api_keys"("id") ON DELETE set null ON UPDATE no action; \ No newline at end of file diff --git a/backend/drizzle/meta/0008_snapshot.json b/backend/drizzle/meta/0008_snapshot.json index 82f59c6..db32480 100644 --- a/backend/drizzle/meta/0008_snapshot.json +++ b/backend/drizzle/meta/0008_snapshot.json @@ -92,9 +92,7 @@ "api_keys_key_unique": { "name": "api_keys_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -224,12 +222,8 @@ "name": "completions_api_key_id_api_keys_id_fk", "tableFrom": "completions", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -237,12 +231,8 @@ "name": "completions_upstream_id_upstreams_id_fk", "tableFrom": "completions", "tableTo": "upstreams", - "columnsFrom": [ - "upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -252,9 +242,7 @@ "completions_id_unique": { "name": "completions_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -366,12 +354,8 @@ "name": "embeddings_api_key_id_api_keys_id_fk", "tableFrom": "embeddings", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -379,12 +363,8 @@ "name": "embeddings_model_id_models_id_fk", "tableFrom": "embeddings", "tableTo": "models", - "columnsFrom": [ - "model_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["model_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -394,9 +374,7 @@ "embeddings_id_unique": { "name": "embeddings_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -509,12 +487,8 @@ "name": "models_provider_id_providers_id_fk", "tableFrom": "models", "tableTo": "providers", - "columnsFrom": [ - "provider_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -524,10 +498,7 @@ "models_provider_system_name_unique": { "name": "models_provider_system_name_unique", "nullsNotDistinct": false, - "columns": [ - "provider_id", - "system_name" - ] + "columns": ["provider_id", "system_name"] } }, "policies": {}, @@ -622,9 +593,7 @@ "providers_name_unique": { "name": "providers_name_unique", "nullsNotDistinct": false, - "columns": [ - "name" - ] + "columns": ["name"] } }, "policies": {}, @@ -679,9 +648,7 @@ "settings_key_unique": { "name": "settings_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -773,12 +740,8 @@ "name": "srv_logs_related_api_key_id_api_keys_id_fk", "tableFrom": "srv_logs", "tableTo": "api_keys", - "columnsFrom": [ - "related_api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -786,12 +749,8 @@ "name": "srv_logs_related_upstream_id_upstreams_id_fk", "tableFrom": "srv_logs", "tableTo": "upstreams", - "columnsFrom": [ - "related_upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -799,12 +758,8 @@ "name": "srv_logs_related_completion_id_completions_id_fk", "tableFrom": "srv_logs", "tableTo": "completions", - "columnsFrom": [ - "related_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -814,9 +769,7 @@ "srv_logs_id_unique": { "name": "srv_logs_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -922,40 +875,22 @@ "public.completions_status": { "name": "completions_status", "schema": "public", - "values": [ - "pending", - "completed", - "failed" - ] + "values": ["pending", "completed", "failed"] }, "public.model_type": { "name": "model_type", "schema": "public", - "values": [ - "chat", - "embedding" - ] + "values": ["chat", "embedding"] }, "public.provider_type": { "name": "provider_type", "schema": "public", - "values": [ - "openai", - "openai-responses", - "anthropic", - "azure", - "ollama" - ] + "values": ["openai", "openai-responses", "anthropic", "azure", "ollama"] }, "public.srv_logs_level": { "name": "srv_logs_level", "schema": "public", - "values": [ - "unspecific", - "info", - "warn", - "error" - ] + "values": ["unspecific", "info", "warn", "error"] } }, "schemas": {}, @@ -968,4 +903,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/backend/drizzle/meta/0009_snapshot.json b/backend/drizzle/meta/0009_snapshot.json index 35891cb..15302ee 100644 --- a/backend/drizzle/meta/0009_snapshot.json +++ b/backend/drizzle/meta/0009_snapshot.json @@ -106,16 +106,12 @@ "api_keys_key_unique": { "name": "api_keys_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] }, "api_keys_external_id_unique": { "name": "api_keys_external_id_unique", "nullsNotDistinct": false, - "columns": [ - "external_id" - ] + "columns": ["external_id"] } }, "policies": {}, @@ -245,12 +241,8 @@ "name": "completions_api_key_id_api_keys_id_fk", "tableFrom": "completions", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -258,12 +250,8 @@ "name": "completions_upstream_id_upstreams_id_fk", "tableFrom": "completions", "tableTo": "upstreams", - "columnsFrom": [ - "upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -273,9 +261,7 @@ "completions_id_unique": { "name": "completions_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -387,12 +373,8 @@ "name": "embeddings_api_key_id_api_keys_id_fk", "tableFrom": "embeddings", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -400,12 +382,8 @@ "name": "embeddings_model_id_models_id_fk", "tableFrom": "embeddings", "tableTo": "models", - "columnsFrom": [ - "model_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["model_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -415,9 +393,7 @@ "embeddings_id_unique": { "name": "embeddings_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -530,12 +506,8 @@ "name": "models_provider_id_providers_id_fk", "tableFrom": "models", "tableTo": "providers", - "columnsFrom": [ - "provider_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -545,10 +517,7 @@ "models_provider_system_name_unique": { "name": "models_provider_system_name_unique", "nullsNotDistinct": false, - "columns": [ - "provider_id", - "system_name" - ] + "columns": ["provider_id", "system_name"] } }, "policies": {}, @@ -643,9 +612,7 @@ "providers_name_unique": { "name": "providers_name_unique", "nullsNotDistinct": false, - "columns": [ - "name" - ] + "columns": ["name"] } }, "policies": {}, @@ -700,9 +667,7 @@ "settings_key_unique": { "name": "settings_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -794,12 +759,8 @@ "name": "srv_logs_related_api_key_id_api_keys_id_fk", "tableFrom": "srv_logs", "tableTo": "api_keys", - "columnsFrom": [ - "related_api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -807,12 +768,8 @@ "name": "srv_logs_related_upstream_id_upstreams_id_fk", "tableFrom": "srv_logs", "tableTo": "upstreams", - "columnsFrom": [ - "related_upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -820,12 +777,8 @@ "name": "srv_logs_related_completion_id_completions_id_fk", "tableFrom": "srv_logs", "tableTo": "completions", - "columnsFrom": [ - "related_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -835,9 +788,7 @@ "srv_logs_id_unique": { "name": "srv_logs_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -943,49 +894,27 @@ "public.api_key_source": { "name": "api_key_source", "schema": "public", - "values": [ - "manual", - "operator", - "init" - ] + "values": ["manual", "operator", "init"] }, "public.completions_status": { "name": "completions_status", "schema": "public", - "values": [ - "pending", - "completed", - "failed" - ] + "values": ["pending", "completed", "failed"] }, "public.model_type": { "name": "model_type", "schema": "public", - "values": [ - "chat", - "embedding" - ] + "values": ["chat", "embedding"] }, "public.provider_type": { "name": "provider_type", "schema": "public", - "values": [ - "openai", - "openai-responses", - "anthropic", - "azure", - "ollama" - ] + "values": ["openai", "openai-responses", "anthropic", "azure", "ollama"] }, "public.srv_logs_level": { "name": "srv_logs_level", "schema": "public", - "values": [ - "unspecific", - "info", - "warn", - "error" - ] + "values": ["unspecific", "info", "warn", "error"] } }, "schemas": {}, @@ -998,4 +927,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/backend/drizzle/meta/0010_snapshot.json b/backend/drizzle/meta/0010_snapshot.json index a39d245..efe1a6b 100644 --- a/backend/drizzle/meta/0010_snapshot.json +++ b/backend/drizzle/meta/0010_snapshot.json @@ -106,16 +106,12 @@ "api_keys_key_unique": { "name": "api_keys_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] }, "api_keys_external_id_unique": { "name": "api_keys_external_id_unique", "nullsNotDistinct": false, - "columns": [ - "external_id" - ] + "columns": ["external_id"] } }, "policies": {}, @@ -245,12 +241,8 @@ "name": "completions_api_key_id_api_keys_id_fk", "tableFrom": "completions", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -258,12 +250,8 @@ "name": "completions_upstream_id_upstreams_id_fk", "tableFrom": "completions", "tableTo": "upstreams", - "columnsFrom": [ - "upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -273,9 +261,7 @@ "completions_id_unique": { "name": "completions_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -387,12 +373,8 @@ "name": "embeddings_api_key_id_api_keys_id_fk", "tableFrom": "embeddings", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -400,12 +382,8 @@ "name": "embeddings_model_id_models_id_fk", "tableFrom": "embeddings", "tableTo": "models", - "columnsFrom": [ - "model_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["model_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -415,9 +393,7 @@ "embeddings_id_unique": { "name": "embeddings_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -530,12 +506,8 @@ "name": "models_provider_id_providers_id_fk", "tableFrom": "models", "tableTo": "providers", - "columnsFrom": [ - "provider_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -545,10 +517,7 @@ "models_provider_system_name_unique": { "name": "models_provider_system_name_unique", "nullsNotDistinct": false, - "columns": [ - "provider_id", - "system_name" - ] + "columns": ["provider_id", "system_name"] } }, "policies": {}, @@ -643,9 +612,7 @@ "providers_name_unique": { "name": "providers_name_unique", "nullsNotDistinct": false, - "columns": [ - "name" - ] + "columns": ["name"] } }, "policies": {}, @@ -700,9 +667,7 @@ "settings_key_unique": { "name": "settings_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -794,12 +759,8 @@ "name": "srv_logs_related_api_key_id_api_keys_id_fk", "tableFrom": "srv_logs", "tableTo": "api_keys", - "columnsFrom": [ - "related_api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -807,12 +768,8 @@ "name": "srv_logs_related_upstream_id_upstreams_id_fk", "tableFrom": "srv_logs", "tableTo": "upstreams", - "columnsFrom": [ - "related_upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -820,12 +777,8 @@ "name": "srv_logs_related_completion_id_completions_id_fk", "tableFrom": "srv_logs", "tableTo": "completions", - "columnsFrom": [ - "related_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -835,9 +788,7 @@ "srv_logs_id_unique": { "name": "srv_logs_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -943,50 +894,27 @@ "public.api_key_source": { "name": "api_key_source", "schema": "public", - "values": [ - "manual", - "operator", - "init" - ] + "values": ["manual", "operator", "init"] }, "public.completions_status": { "name": "completions_status", "schema": "public", - "values": [ - "pending", - "completed", - "failed", - "aborted" - ] + "values": ["pending", "completed", "failed", "aborted"] }, "public.model_type": { "name": "model_type", "schema": "public", - "values": [ - "chat", - "embedding" - ] + "values": ["chat", "embedding"] }, "public.provider_type": { "name": "provider_type", "schema": "public", - "values": [ - "openai", - "openai-responses", - "anthropic", - "azure", - "ollama" - ] + "values": ["openai", "openai-responses", "anthropic", "azure", "ollama"] }, "public.srv_logs_level": { "name": "srv_logs_level", "schema": "public", - "values": [ - "unspecific", - "info", - "warn", - "error" - ] + "values": ["unspecific", "info", "warn", "error"] } }, "schemas": {}, @@ -999,4 +927,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/backend/drizzle/meta/0011_snapshot.json b/backend/drizzle/meta/0011_snapshot.json index 2df7511..fbc0ca3 100644 --- a/backend/drizzle/meta/0011_snapshot.json +++ b/backend/drizzle/meta/0011_snapshot.json @@ -106,16 +106,12 @@ "api_keys_key_unique": { "name": "api_keys_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] }, "api_keys_external_id_unique": { "name": "api_keys_external_id_unique", "nullsNotDistinct": false, - "columns": [ - "external_id" - ] + "columns": ["external_id"] } }, "policies": {}, @@ -269,12 +265,8 @@ "name": "completions_api_key_id_api_keys_id_fk", "tableFrom": "completions", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -282,12 +274,8 @@ "name": "completions_upstream_id_upstreams_id_fk", "tableFrom": "completions", "tableTo": "upstreams", - "columnsFrom": [ - "upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -295,12 +283,8 @@ "name": "completions_source_completion_id_completions_id_fk", "tableFrom": "completions", "tableTo": "completions", - "columnsFrom": [ - "source_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["source_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -310,9 +294,7 @@ "completions_id_unique": { "name": "completions_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -424,12 +406,8 @@ "name": "embeddings_api_key_id_api_keys_id_fk", "tableFrom": "embeddings", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -437,12 +415,8 @@ "name": "embeddings_model_id_models_id_fk", "tableFrom": "embeddings", "tableTo": "models", - "columnsFrom": [ - "model_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["model_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -452,9 +426,7 @@ "embeddings_id_unique": { "name": "embeddings_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -567,12 +539,8 @@ "name": "models_provider_id_providers_id_fk", "tableFrom": "models", "tableTo": "providers", - "columnsFrom": [ - "provider_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -582,10 +550,7 @@ "models_provider_system_name_unique": { "name": "models_provider_system_name_unique", "nullsNotDistinct": false, - "columns": [ - "provider_id", - "system_name" - ] + "columns": ["provider_id", "system_name"] } }, "policies": {}, @@ -680,9 +645,7 @@ "providers_name_unique": { "name": "providers_name_unique", "nullsNotDistinct": false, - "columns": [ - "name" - ] + "columns": ["name"] } }, "policies": {}, @@ -737,9 +700,7 @@ "settings_key_unique": { "name": "settings_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -831,12 +792,8 @@ "name": "srv_logs_related_api_key_id_api_keys_id_fk", "tableFrom": "srv_logs", "tableTo": "api_keys", - "columnsFrom": [ - "related_api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -844,12 +801,8 @@ "name": "srv_logs_related_upstream_id_upstreams_id_fk", "tableFrom": "srv_logs", "tableTo": "upstreams", - "columnsFrom": [ - "related_upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -857,12 +810,8 @@ "name": "srv_logs_related_completion_id_completions_id_fk", "tableFrom": "srv_logs", "tableTo": "completions", - "columnsFrom": [ - "related_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -872,9 +821,7 @@ "srv_logs_id_unique": { "name": "srv_logs_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -980,51 +927,27 @@ "public.api_key_source": { "name": "api_key_source", "schema": "public", - "values": [ - "manual", - "operator", - "init" - ] + "values": ["manual", "operator", "init"] }, "public.completions_status": { "name": "completions_status", "schema": "public", - "values": [ - "pending", - "completed", - "failed", - "aborted", - "cache_hit" - ] + "values": ["pending", "completed", "failed", "aborted", "cache_hit"] }, "public.model_type": { "name": "model_type", "schema": "public", - "values": [ - "chat", - "embedding" - ] + "values": ["chat", "embedding"] }, "public.provider_type": { "name": "provider_type", "schema": "public", - "values": [ - "openai", - "openai-responses", - "anthropic", - "azure", - "ollama" - ] + "values": ["openai", "openai-responses", "anthropic", "azure", "ollama"] }, "public.srv_logs_level": { "name": "srv_logs_level", "schema": "public", - "values": [ - "unspecific", - "info", - "warn", - "error" - ] + "values": ["unspecific", "info", "warn", "error"] } }, "schemas": {}, @@ -1037,4 +960,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/backend/drizzle/meta/0012_snapshot.json b/backend/drizzle/meta/0012_snapshot.json index 37bc350..d0a68a0 100644 --- a/backend/drizzle/meta/0012_snapshot.json +++ b/backend/drizzle/meta/0012_snapshot.json @@ -106,16 +106,12 @@ "api_keys_key_unique": { "name": "api_keys_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] }, "api_keys_external_id_unique": { "name": "api_keys_external_id_unique", "nullsNotDistinct": false, - "columns": [ - "external_id" - ] + "columns": ["external_id"] } }, "policies": {}, @@ -269,12 +265,8 @@ "name": "completions_api_key_id_api_keys_id_fk", "tableFrom": "completions", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -282,12 +274,8 @@ "name": "completions_upstream_id_upstreams_id_fk", "tableFrom": "completions", "tableTo": "upstreams", - "columnsFrom": [ - "upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -295,12 +283,8 @@ "name": "completions_source_completion_id_completions_id_fk", "tableFrom": "completions", "tableTo": "completions", - "columnsFrom": [ - "source_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["source_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -310,9 +294,7 @@ "completions_id_unique": { "name": "completions_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -424,12 +406,8 @@ "name": "embeddings_api_key_id_api_keys_id_fk", "tableFrom": "embeddings", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -437,12 +415,8 @@ "name": "embeddings_model_id_models_id_fk", "tableFrom": "embeddings", "tableTo": "models", - "columnsFrom": [ - "model_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["model_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -452,9 +426,7 @@ "embeddings_id_unique": { "name": "embeddings_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -567,12 +539,8 @@ "name": "models_provider_id_providers_id_fk", "tableFrom": "models", "tableTo": "providers", - "columnsFrom": [ - "provider_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -582,10 +550,7 @@ "models_provider_system_name_unique": { "name": "models_provider_system_name_unique", "nullsNotDistinct": false, - "columns": [ - "provider_id", - "system_name" - ] + "columns": ["provider_id", "system_name"] } }, "policies": {}, @@ -729,9 +694,7 @@ "settings_key_unique": { "name": "settings_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -823,12 +786,8 @@ "name": "srv_logs_related_api_key_id_api_keys_id_fk", "tableFrom": "srv_logs", "tableTo": "api_keys", - "columnsFrom": [ - "related_api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -836,12 +795,8 @@ "name": "srv_logs_related_upstream_id_upstreams_id_fk", "tableFrom": "srv_logs", "tableTo": "upstreams", - "columnsFrom": [ - "related_upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -849,12 +804,8 @@ "name": "srv_logs_related_completion_id_completions_id_fk", "tableFrom": "srv_logs", "tableTo": "completions", - "columnsFrom": [ - "related_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -864,9 +815,7 @@ "srv_logs_id_unique": { "name": "srv_logs_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -972,51 +921,27 @@ "public.api_key_source": { "name": "api_key_source", "schema": "public", - "values": [ - "manual", - "operator", - "init" - ] + "values": ["manual", "operator", "init"] }, "public.completions_status": { "name": "completions_status", "schema": "public", - "values": [ - "pending", - "completed", - "failed", - "aborted", - "cache_hit" - ] + "values": ["pending", "completed", "failed", "aborted", "cache_hit"] }, "public.model_type": { "name": "model_type", "schema": "public", - "values": [ - "chat", - "embedding" - ] + "values": ["chat", "embedding"] }, "public.provider_type": { "name": "provider_type", "schema": "public", - "values": [ - "openai", - "openai-responses", - "anthropic", - "azure", - "ollama" - ] + "values": ["openai", "openai-responses", "anthropic", "azure", "ollama"] }, "public.srv_logs_level": { "name": "srv_logs_level", "schema": "public", - "values": [ - "unspecific", - "info", - "warn", - "error" - ] + "values": ["unspecific", "info", "warn", "error"] } }, "schemas": {}, @@ -1029,4 +954,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/backend/drizzle/meta/0013_snapshot.json b/backend/drizzle/meta/0013_snapshot.json index 076f16b..c4956ca 100644 --- a/backend/drizzle/meta/0013_snapshot.json +++ b/backend/drizzle/meta/0013_snapshot.json @@ -134,12 +134,8 @@ "name": "alert_history_rule_id_alert_rules_id_fk", "tableFrom": "alert_history", "tableTo": "alert_rules", - "columnsFrom": [ - "rule_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["rule_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -335,16 +331,12 @@ "api_keys_key_unique": { "name": "api_keys_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] }, "api_keys_external_id_unique": { "name": "api_keys_external_id_unique", "nullsNotDistinct": false, - "columns": [ - "external_id" - ] + "columns": ["external_id"] } }, "policies": {}, @@ -498,12 +490,8 @@ "name": "completions_api_key_id_api_keys_id_fk", "tableFrom": "completions", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -511,12 +499,8 @@ "name": "completions_upstream_id_upstreams_id_fk", "tableFrom": "completions", "tableTo": "upstreams", - "columnsFrom": [ - "upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -524,12 +508,8 @@ "name": "completions_source_completion_id_completions_id_fk", "tableFrom": "completions", "tableTo": "completions", - "columnsFrom": [ - "source_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["source_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -539,9 +519,7 @@ "completions_id_unique": { "name": "completions_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -653,12 +631,8 @@ "name": "embeddings_api_key_id_api_keys_id_fk", "tableFrom": "embeddings", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -666,12 +640,8 @@ "name": "embeddings_model_id_models_id_fk", "tableFrom": "embeddings", "tableTo": "models", - "columnsFrom": [ - "model_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["model_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -681,9 +651,7 @@ "embeddings_id_unique": { "name": "embeddings_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -796,12 +764,8 @@ "name": "models_provider_id_providers_id_fk", "tableFrom": "models", "tableTo": "providers", - "columnsFrom": [ - "provider_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -811,10 +775,7 @@ "models_provider_system_name_unique": { "name": "models_provider_system_name_unique", "nullsNotDistinct": false, - "columns": [ - "provider_id", - "system_name" - ] + "columns": ["provider_id", "system_name"] } }, "policies": {}, @@ -958,9 +919,7 @@ "settings_key_unique": { "name": "settings_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -1052,12 +1011,8 @@ "name": "srv_logs_related_api_key_id_api_keys_id_fk", "tableFrom": "srv_logs", "tableTo": "api_keys", - "columnsFrom": [ - "related_api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1065,12 +1020,8 @@ "name": "srv_logs_related_upstream_id_upstreams_id_fk", "tableFrom": "srv_logs", "tableTo": "upstreams", - "columnsFrom": [ - "related_upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1078,12 +1029,8 @@ "name": "srv_logs_related_completion_id_completions_id_fk", "tableFrom": "srv_logs", "tableTo": "completions", - "columnsFrom": [ - "related_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -1093,9 +1040,7 @@ "srv_logs_id_unique": { "name": "srv_logs_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -1201,79 +1146,42 @@ "public.alert_channel_type": { "name": "alert_channel_type", "schema": "public", - "values": [ - "webhook", - "email", - "feishu" - ] + "values": ["webhook", "email", "feishu"] }, "public.alert_history_status": { "name": "alert_history_status", "schema": "public", - "values": [ - "sent", - "failed", - "suppressed" - ] + "values": ["sent", "failed", "suppressed"] }, "public.alert_rule_type": { "name": "alert_rule_type", "schema": "public", - "values": [ - "budget", - "error_rate", - "latency", - "quota" - ] + "values": ["budget", "error_rate", "latency", "quota"] }, "public.api_key_source": { "name": "api_key_source", "schema": "public", - "values": [ - "manual", - "operator", - "init" - ] + "values": ["manual", "operator", "init"] }, "public.completions_status": { "name": "completions_status", "schema": "public", - "values": [ - "pending", - "completed", - "failed", - "aborted", - "cache_hit" - ] + "values": ["pending", "completed", "failed", "aborted", "cache_hit"] }, "public.model_type": { "name": "model_type", "schema": "public", - "values": [ - "chat", - "embedding" - ] + "values": ["chat", "embedding"] }, "public.provider_type": { "name": "provider_type", "schema": "public", - "values": [ - "openai", - "openai-responses", - "anthropic", - "azure", - "ollama" - ] + "values": ["openai", "openai-responses", "anthropic", "azure", "ollama"] }, "public.srv_logs_level": { "name": "srv_logs_level", "schema": "public", - "values": [ - "unspecific", - "info", - "warn", - "error" - ] + "values": ["unspecific", "info", "warn", "error"] } }, "schemas": {}, @@ -1286,4 +1194,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/backend/drizzle/meta/0014_snapshot.json b/backend/drizzle/meta/0014_snapshot.json index 15269fe..0f82021 100644 --- a/backend/drizzle/meta/0014_snapshot.json +++ b/backend/drizzle/meta/0014_snapshot.json @@ -152,12 +152,8 @@ "name": "alert_history_rule_id_alert_rules_id_fk", "tableFrom": "alert_history", "tableTo": "alert_rules", - "columnsFrom": [ - "rule_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["rule_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -371,16 +367,12 @@ "api_keys_key_unique": { "name": "api_keys_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] }, "api_keys_external_id_unique": { "name": "api_keys_external_id_unique", "nullsNotDistinct": false, - "columns": [ - "external_id" - ] + "columns": ["external_id"] } }, "policies": {}, @@ -534,12 +526,8 @@ "name": "completions_api_key_id_api_keys_id_fk", "tableFrom": "completions", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -547,12 +535,8 @@ "name": "completions_upstream_id_upstreams_id_fk", "tableFrom": "completions", "tableTo": "upstreams", - "columnsFrom": [ - "upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -560,12 +544,8 @@ "name": "completions_source_completion_id_completions_id_fk", "tableFrom": "completions", "tableTo": "completions", - "columnsFrom": [ - "source_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["source_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -575,9 +555,7 @@ "completions_id_unique": { "name": "completions_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -689,12 +667,8 @@ "name": "embeddings_api_key_id_api_keys_id_fk", "tableFrom": "embeddings", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -702,12 +676,8 @@ "name": "embeddings_model_id_models_id_fk", "tableFrom": "embeddings", "tableTo": "models", - "columnsFrom": [ - "model_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["model_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -717,9 +687,7 @@ "embeddings_id_unique": { "name": "embeddings_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -832,12 +800,8 @@ "name": "models_provider_id_providers_id_fk", "tableFrom": "models", "tableTo": "providers", - "columnsFrom": [ - "provider_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -847,10 +811,7 @@ "models_provider_system_name_unique": { "name": "models_provider_system_name_unique", "nullsNotDistinct": false, - "columns": [ - "provider_id", - "system_name" - ] + "columns": ["provider_id", "system_name"] } }, "policies": {}, @@ -994,9 +955,7 @@ "settings_key_unique": { "name": "settings_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -1088,12 +1047,8 @@ "name": "srv_logs_related_api_key_id_api_keys_id_fk", "tableFrom": "srv_logs", "tableTo": "api_keys", - "columnsFrom": [ - "related_api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1101,12 +1056,8 @@ "name": "srv_logs_related_upstream_id_upstreams_id_fk", "tableFrom": "srv_logs", "tableTo": "upstreams", - "columnsFrom": [ - "related_upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1114,12 +1065,8 @@ "name": "srv_logs_related_completion_id_completions_id_fk", "tableFrom": "srv_logs", "tableTo": "completions", - "columnsFrom": [ - "related_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -1129,9 +1076,7 @@ "srv_logs_id_unique": { "name": "srv_logs_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -1237,79 +1182,42 @@ "public.alert_channel_type": { "name": "alert_channel_type", "schema": "public", - "values": [ - "webhook", - "email", - "feishu" - ] + "values": ["webhook", "email", "feishu"] }, "public.alert_history_status": { "name": "alert_history_status", "schema": "public", - "values": [ - "sent", - "failed", - "suppressed" - ] + "values": ["sent", "failed", "suppressed"] }, "public.alert_rule_type": { "name": "alert_rule_type", "schema": "public", - "values": [ - "budget", - "error_rate", - "latency", - "quota" - ] + "values": ["budget", "error_rate", "latency", "quota"] }, "public.api_key_source": { "name": "api_key_source", "schema": "public", - "values": [ - "manual", - "operator", - "init" - ] + "values": ["manual", "operator", "init"] }, "public.completions_status": { "name": "completions_status", "schema": "public", - "values": [ - "pending", - "completed", - "failed", - "aborted", - "cache_hit" - ] + "values": ["pending", "completed", "failed", "aborted", "cache_hit"] }, "public.model_type": { "name": "model_type", "schema": "public", - "values": [ - "chat", - "embedding" - ] + "values": ["chat", "embedding"] }, "public.provider_type": { "name": "provider_type", "schema": "public", - "values": [ - "openai", - "openai-responses", - "anthropic", - "azure", - "ollama" - ] + "values": ["openai", "openai-responses", "anthropic", "azure", "ollama"] }, "public.srv_logs_level": { "name": "srv_logs_level", "schema": "public", - "values": [ - "unspecific", - "info", - "warn", - "error" - ] + "values": ["unspecific", "info", "warn", "error"] } }, "schemas": {}, @@ -1322,4 +1230,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/backend/drizzle/meta/0015_snapshot.json b/backend/drizzle/meta/0015_snapshot.json index 0186b8e..d511330 100644 --- a/backend/drizzle/meta/0015_snapshot.json +++ b/backend/drizzle/meta/0015_snapshot.json @@ -152,12 +152,8 @@ "name": "alert_history_rule_id_alert_rules_id_fk", "tableFrom": "alert_history", "tableTo": "alert_rules", - "columnsFrom": [ - "rule_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["rule_id"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -371,16 +367,12 @@ "api_keys_key_unique": { "name": "api_keys_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] }, "api_keys_external_id_unique": { "name": "api_keys_external_id_unique", "nullsNotDistinct": false, - "columns": [ - "external_id" - ] + "columns": ["external_id"] } }, "policies": {}, @@ -534,12 +526,8 @@ "name": "completions_api_key_id_api_keys_id_fk", "tableFrom": "completions", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -547,12 +535,8 @@ "name": "completions_upstream_id_upstreams_id_fk", "tableFrom": "completions", "tableTo": "upstreams", - "columnsFrom": [ - "upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -560,12 +544,8 @@ "name": "completions_source_completion_id_completions_id_fk", "tableFrom": "completions", "tableTo": "completions", - "columnsFrom": [ - "source_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["source_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -575,9 +555,7 @@ "completions_id_unique": { "name": "completions_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -689,12 +667,8 @@ "name": "embeddings_api_key_id_api_keys_id_fk", "tableFrom": "embeddings", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -702,12 +676,8 @@ "name": "embeddings_model_id_models_id_fk", "tableFrom": "embeddings", "tableTo": "models", - "columnsFrom": [ - "model_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["model_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -717,9 +687,7 @@ "embeddings_id_unique": { "name": "embeddings_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -832,12 +800,8 @@ "name": "models_provider_id_providers_id_fk", "tableFrom": "models", "tableTo": "providers", - "columnsFrom": [ - "provider_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -847,10 +811,7 @@ "models_provider_system_name_unique": { "name": "models_provider_system_name_unique", "nullsNotDistinct": false, - "columns": [ - "provider_id", - "system_name" - ] + "columns": ["provider_id", "system_name"] } }, "policies": {}, @@ -994,9 +955,7 @@ "settings_key_unique": { "name": "settings_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -1088,12 +1047,8 @@ "name": "srv_logs_related_api_key_id_api_keys_id_fk", "tableFrom": "srv_logs", "tableTo": "api_keys", - "columnsFrom": [ - "related_api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1101,12 +1056,8 @@ "name": "srv_logs_related_upstream_id_upstreams_id_fk", "tableFrom": "srv_logs", "tableTo": "upstreams", - "columnsFrom": [ - "related_upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1114,12 +1065,8 @@ "name": "srv_logs_related_completion_id_completions_id_fk", "tableFrom": "srv_logs", "tableTo": "completions", - "columnsFrom": [ - "related_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -1129,9 +1076,7 @@ "srv_logs_id_unique": { "name": "srv_logs_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -1237,79 +1182,42 @@ "public.alert_channel_type": { "name": "alert_channel_type", "schema": "public", - "values": [ - "webhook", - "email", - "feishu" - ] + "values": ["webhook", "email", "feishu"] }, "public.alert_history_status": { "name": "alert_history_status", "schema": "public", - "values": [ - "sent", - "failed", - "suppressed" - ] + "values": ["sent", "failed", "suppressed"] }, "public.alert_rule_type": { "name": "alert_rule_type", "schema": "public", - "values": [ - "budget", - "error_rate", - "latency", - "quota" - ] + "values": ["budget", "error_rate", "latency", "quota"] }, "public.api_key_source": { "name": "api_key_source", "schema": "public", - "values": [ - "manual", - "operator", - "init" - ] + "values": ["manual", "operator", "init"] }, "public.completions_status": { "name": "completions_status", "schema": "public", - "values": [ - "pending", - "completed", - "failed", - "aborted", - "cache_hit" - ] + "values": ["pending", "completed", "failed", "aborted", "cache_hit"] }, "public.model_type": { "name": "model_type", "schema": "public", - "values": [ - "chat", - "embedding" - ] + "values": ["chat", "embedding"] }, "public.provider_type": { "name": "provider_type", "schema": "public", - "values": [ - "openai", - "openai-responses", - "anthropic", - "azure", - "ollama" - ] + "values": ["openai", "openai-responses", "anthropic", "azure", "ollama"] }, "public.srv_logs_level": { "name": "srv_logs_level", "schema": "public", - "values": [ - "unspecific", - "info", - "warn", - "error" - ] + "values": ["unspecific", "info", "warn", "error"] } }, "schemas": {}, @@ -1322,4 +1230,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/backend/drizzle/meta/0016_snapshot.json b/backend/drizzle/meta/0016_snapshot.json index 3070dbe..0b4c782 100644 --- a/backend/drizzle/meta/0016_snapshot.json +++ b/backend/drizzle/meta/0016_snapshot.json @@ -152,12 +152,8 @@ "name": "alert_history_rule_id_alert_rules_id_fk", "tableFrom": "alert_history", "tableTo": "alert_rules", - "columnsFrom": [ - "rule_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["rule_id"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -371,16 +367,12 @@ "api_keys_key_unique": { "name": "api_keys_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] }, "api_keys_external_id_unique": { "name": "api_keys_external_id_unique", "nullsNotDistinct": false, - "columns": [ - "external_id" - ] + "columns": ["external_id"] } }, "policies": {}, @@ -534,12 +526,8 @@ "name": "completions_api_key_id_api_keys_id_fk", "tableFrom": "completions", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -547,12 +535,8 @@ "name": "completions_upstream_id_upstreams_id_fk", "tableFrom": "completions", "tableTo": "upstreams", - "columnsFrom": [ - "upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -560,12 +544,8 @@ "name": "completions_source_completion_id_completions_id_fk", "tableFrom": "completions", "tableTo": "completions", - "columnsFrom": [ - "source_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["source_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -575,9 +555,7 @@ "completions_id_unique": { "name": "completions_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -689,12 +667,8 @@ "name": "embeddings_api_key_id_api_keys_id_fk", "tableFrom": "embeddings", "tableTo": "api_keys", - "columnsFrom": [ - "api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -702,12 +676,8 @@ "name": "embeddings_model_id_models_id_fk", "tableFrom": "embeddings", "tableTo": "models", - "columnsFrom": [ - "model_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["model_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -717,9 +687,7 @@ "embeddings_id_unique": { "name": "embeddings_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -832,12 +800,8 @@ "name": "models_provider_id_providers_id_fk", "tableFrom": "models", "tableTo": "providers", - "columnsFrom": [ - "provider_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -847,10 +811,7 @@ "models_provider_system_name_unique": { "name": "models_provider_system_name_unique", "nullsNotDistinct": false, - "columns": [ - "provider_id", - "system_name" - ] + "columns": ["provider_id", "system_name"] } }, "policies": {}, @@ -1007,9 +968,7 @@ "settings_key_unique": { "name": "settings_key_unique", "nullsNotDistinct": false, - "columns": [ - "key" - ] + "columns": ["key"] } }, "policies": {}, @@ -1101,12 +1060,8 @@ "name": "srv_logs_related_api_key_id_api_keys_id_fk", "tableFrom": "srv_logs", "tableTo": "api_keys", - "columnsFrom": [ - "related_api_key_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_api_key_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1114,12 +1069,8 @@ "name": "srv_logs_related_upstream_id_upstreams_id_fk", "tableFrom": "srv_logs", "tableTo": "upstreams", - "columnsFrom": [ - "related_upstream_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_upstream_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1127,12 +1078,8 @@ "name": "srv_logs_related_completion_id_completions_id_fk", "tableFrom": "srv_logs", "tableTo": "completions", - "columnsFrom": [ - "related_completion_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["related_completion_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -1142,9 +1089,7 @@ "srv_logs_id_unique": { "name": "srv_logs_id_unique", "nullsNotDistinct": false, - "columns": [ - "id" - ] + "columns": ["id"] } }, "policies": {}, @@ -1250,79 +1195,42 @@ "public.alert_channel_type": { "name": "alert_channel_type", "schema": "public", - "values": [ - "webhook", - "email", - "feishu" - ] + "values": ["webhook", "email", "feishu"] }, "public.alert_history_status": { "name": "alert_history_status", "schema": "public", - "values": [ - "sent", - "failed", - "suppressed" - ] + "values": ["sent", "failed", "suppressed"] }, "public.alert_rule_type": { "name": "alert_rule_type", "schema": "public", - "values": [ - "budget", - "error_rate", - "latency", - "quota" - ] + "values": ["budget", "error_rate", "latency", "quota"] }, "public.api_key_source": { "name": "api_key_source", "schema": "public", - "values": [ - "manual", - "operator", - "init" - ] + "values": ["manual", "operator", "init"] }, "public.completions_status": { "name": "completions_status", "schema": "public", - "values": [ - "pending", - "completed", - "failed", - "aborted", - "cache_hit" - ] + "values": ["pending", "completed", "failed", "aborted", "cache_hit"] }, "public.model_type": { "name": "model_type", "schema": "public", - "values": [ - "chat", - "embedding" - ] + "values": ["chat", "embedding"] }, "public.provider_type": { "name": "provider_type", "schema": "public", - "values": [ - "openai", - "openai-responses", - "anthropic", - "azure", - "ollama" - ] + "values": ["openai", "openai-responses", "anthropic", "azure", "ollama"] }, "public.srv_logs_level": { "name": "srv_logs_level", "schema": "public", - "values": [ - "unspecific", - "info", - "warn", - "error" - ] + "values": ["unspecific", "info", "warn", "error"] } }, "schemas": {}, @@ -1335,4 +1243,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/backend/drizzle/meta/0017_snapshot.json b/backend/drizzle/meta/0017_snapshot.json new file mode 100644 index 0000000..52e6885 --- /dev/null +++ b/backend/drizzle/meta/0017_snapshot.json @@ -0,0 +1,1703 @@ +{ + "id": "121f3aef-943b-4b22-ad2a-83c8a9971534", + "prevId": "c4be367c-3cfb-4b6f-ab4b-5e708dd70da4", + "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.playground_conversations": { + "name": "playground_conversations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_conversations_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(63)", + "primaryKey": false, + "notNull": true + }, + "api_key_id": { + "name": "api_key_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "params": { + "name": "params", + "type": "jsonb", + "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": { + "playground_conversations_api_key_id_api_keys_id_fk": { + "name": "playground_conversations_api_key_id_api_keys_id_fk", + "tableFrom": "playground_conversations", + "tableTo": "api_keys", + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playground_messages": { + "name": "playground_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_messages_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "conversation_id": { + "name": "conversation_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "playground_message_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "completion_id": { + "name": "completion_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "playground_messages_conversation_id_playground_conversations_id_fk": { + "name": "playground_messages_conversation_id_playground_conversations_id_fk", + "tableFrom": "playground_messages", + "tableTo": "playground_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "playground_messages_completion_id_completions_id_fk": { + "name": "playground_messages_completion_id_completions_id_fk", + "tableFrom": "playground_messages", + "tableTo": "completions", + "columnsFrom": ["completion_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playground_test_cases": { + "name": "playground_test_cases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_test_cases_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "messages": { + "name": "messages", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "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.playground_test_results": { + "name": "playground_test_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_test_results_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "test_run_id": { + "name": "test_run_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(63)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "playground_test_result_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "response": { + "name": "response", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "prompt_tokens": { + "name": "prompt_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "completion_tokens": { + "name": "completion_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ttft": { + "name": "ttft", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "completion_id": { + "name": "completion_id", + "type": "integer", + "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()" + } + }, + "indexes": {}, + "foreignKeys": { + "playground_test_results_test_run_id_playground_test_runs_id_fk": { + "name": "playground_test_results_test_run_id_playground_test_runs_id_fk", + "tableFrom": "playground_test_results", + "tableTo": "playground_test_runs", + "columnsFrom": ["test_run_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "playground_test_results_completion_id_completions_id_fk": { + "name": "playground_test_results_completion_id_completions_id_fk", + "tableFrom": "playground_test_results", + "tableTo": "completions", + "columnsFrom": ["completion_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playground_test_runs": { + "name": "playground_test_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_test_runs_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "test_case_id": { + "name": "test_case_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "api_key_id": { + "name": "api_key_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "models": { + "name": "models", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "playground_test_runs_test_case_id_playground_test_cases_id_fk": { + "name": "playground_test_runs_test_case_id_playground_test_cases_id_fk", + "tableFrom": "playground_test_runs", + "tableTo": "playground_test_cases", + "columnsFrom": ["test_case_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "playground_test_runs_api_key_id_api_keys_id_fk": { + "name": "playground_test_runs_api_key_id_api_keys_id_fk", + "tableFrom": "playground_test_runs", + "tableTo": "api_keys", + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "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 + }, + "proxy_url": { + "name": "proxy_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "proxy_enabled": { + "name": "proxy_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": 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.playground_message_role": { + "name": "playground_message_role", + "schema": "public", + "values": ["system", "user", "assistant"] + }, + "public.playground_test_result_status": { + "name": "playground_test_result_status", + "schema": "public", + "values": ["pending", "running", "completed", "failed"] + }, + "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": {} + } +} diff --git a/backend/drizzle/meta/0018_snapshot.json b/backend/drizzle/meta/0018_snapshot.json new file mode 100644 index 0000000..10216d5 --- /dev/null +++ b/backend/drizzle/meta/0018_snapshot.json @@ -0,0 +1,1703 @@ +{ + "id": "922737a2-24e8-497d-8637-f7a14ccfd3db", + "prevId": "121f3aef-943b-4b22-ad2a-83c8a9971534", + "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.playground_conversations": { + "name": "playground_conversations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_conversations_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(63)", + "primaryKey": false, + "notNull": true + }, + "api_key_id": { + "name": "api_key_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "params": { + "name": "params", + "type": "jsonb", + "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": { + "playground_conversations_api_key_id_api_keys_id_fk": { + "name": "playground_conversations_api_key_id_api_keys_id_fk", + "tableFrom": "playground_conversations", + "tableTo": "api_keys", + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playground_messages": { + "name": "playground_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_messages_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "conversation_id": { + "name": "conversation_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "playground_message_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "completion_id": { + "name": "completion_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "playground_messages_conversation_id_playground_conversations_id_fk": { + "name": "playground_messages_conversation_id_playground_conversations_id_fk", + "tableFrom": "playground_messages", + "tableTo": "playground_conversations", + "columnsFrom": ["conversation_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playground_messages_completion_id_completions_id_fk": { + "name": "playground_messages_completion_id_completions_id_fk", + "tableFrom": "playground_messages", + "tableTo": "completions", + "columnsFrom": ["completion_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playground_test_cases": { + "name": "playground_test_cases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_test_cases_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "title": { + "name": "title", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "messages": { + "name": "messages", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "params": { + "name": "params", + "type": "jsonb", + "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.playground_test_results": { + "name": "playground_test_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_test_results_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "test_run_id": { + "name": "test_run_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "varchar(63)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "playground_test_result_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "response": { + "name": "response", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "prompt_tokens": { + "name": "prompt_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "completion_tokens": { + "name": "completion_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ttft": { + "name": "ttft", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "error_message": { + "name": "error_message", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "completion_id": { + "name": "completion_id", + "type": "integer", + "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()" + } + }, + "indexes": {}, + "foreignKeys": { + "playground_test_results_test_run_id_playground_test_runs_id_fk": { + "name": "playground_test_results_test_run_id_playground_test_runs_id_fk", + "tableFrom": "playground_test_results", + "tableTo": "playground_test_runs", + "columnsFrom": ["test_run_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playground_test_results_completion_id_completions_id_fk": { + "name": "playground_test_results_completion_id_completions_id_fk", + "tableFrom": "playground_test_results", + "tableTo": "completions", + "columnsFrom": ["completion_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playground_test_runs": { + "name": "playground_test_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "playground_test_runs_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "test_case_id": { + "name": "test_case_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "api_key_id": { + "name": "api_key_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "models": { + "name": "models", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "playground_test_runs_test_case_id_playground_test_cases_id_fk": { + "name": "playground_test_runs_test_case_id_playground_test_cases_id_fk", + "tableFrom": "playground_test_runs", + "tableTo": "playground_test_cases", + "columnsFrom": ["test_case_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playground_test_runs_api_key_id_api_keys_id_fk": { + "name": "playground_test_runs_api_key_id_api_keys_id_fk", + "tableFrom": "playground_test_runs", + "tableTo": "api_keys", + "columnsFrom": ["api_key_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "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 + }, + "proxy_url": { + "name": "proxy_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "proxy_enabled": { + "name": "proxy_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": 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.playground_message_role": { + "name": "playground_message_role", + "schema": "public", + "values": ["system", "user", "assistant"] + }, + "public.playground_test_result_status": { + "name": "playground_test_result_status", + "schema": "public", + "values": ["pending", "running", "completed", "failed"] + }, + "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": {} + } +} diff --git a/backend/drizzle/meta/_journal.json b/backend/drizzle/meta/_journal.json index ab1f01e..c6d0bab 100644 --- a/backend/drizzle/meta/_journal.json +++ b/backend/drizzle/meta/_journal.json @@ -120,6 +120,20 @@ "when": 1770388002137, "tag": "0016_ambiguous_jigsaw", "breakpoints": true + }, + { + "idx": 17, + "version": "7", + "when": 1770410985101, + "tag": "0017_superb_sentinels", + "breakpoints": true + }, + { + "idx": 18, + "version": "7", + "when": 1770452635977, + "tag": "0018_messy_spectrum", + "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/backend/src/adapters/types.ts b/backend/src/adapters/types.ts index ee06666..1877a9b 100644 --- a/backend/src/adapters/types.ts +++ b/backend/src/adapters/types.ts @@ -234,7 +234,11 @@ export interface InternalStreamChunk { contentBlock?: InternalContentBlock; /** Delta content (for content_block_delta) */ delta?: { - type: "text_delta" | "thinking_delta" | "signature_delta" | "input_json_delta"; + type: + | "text_delta" + | "thinking_delta" + | "signature_delta" + | "input_json_delta"; text?: string; thinking?: string; signature?: string; diff --git a/backend/src/adapters/upstream/anthropic.ts b/backend/src/adapters/upstream/anthropic.ts index 5c4f61d..c236592 100644 --- a/backend/src/adapters/upstream/anthropic.ts +++ b/backend/src/adapters/upstream/anthropic.ts @@ -461,7 +461,8 @@ 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 index 09731fe..b578f20 100644 --- a/backend/src/api/admin/alerts.ts +++ b/backend/src/api/admin/alerts.ts @@ -1,4 +1,5 @@ import { Elysia, t } from "elysia"; +import type { AlertChannelConfig, AlertCondition } from "@/db/schema"; import { deleteAlertChannel, deleteAlertRule, @@ -13,10 +14,6 @@ import { updateAlertRule, } from "@/db"; import { sendTestNotification } from "@/services/alertDispatcher"; -import type { - AlertChannelConfig, - AlertCondition, -} from "@/db/schema"; export const adminAlerts = new Elysia({ prefix: "/alerts" }) // ============================================ @@ -303,7 +300,8 @@ export const adminAlerts = new Elysia({ prefix: "/alerts" }) ruleId: t.Optional(t.Numeric()), }), detail: { - description: "List alert history (paginated, optionally filtered by rule)", + 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 index 8e6463b..e2f79c8 100644 --- a/backend/src/api/admin/grafana.ts +++ b/backend/src/api/admin/grafana.ts @@ -6,12 +6,12 @@ import { listAlertRules, listAlertChannels, } from "@/db"; -import { createLogger } from "@/utils/logger"; import { syncRulesToGrafana, syncChannelsToGrafana, syncAllToGrafana, } from "@/services/grafanaSync"; +import { createLogger } from "@/utils/logger"; const logger = createLogger("adminGrafana"); @@ -208,8 +208,7 @@ export const adminGrafana = new Elysia({ prefix: "/grafana" }) }, { detail: { - description: - "Sync all alert rules and channels to Grafana.", + description: "Sync all alert rules and channels to Grafana.", tags: ["Admin - Grafana"], }, }, @@ -283,8 +282,7 @@ export const adminGrafana = new Elysia({ prefix: "/grafana" }) }, { detail: { - description: - "Get Grafana sync status for all rules and channels.", + description: "Get Grafana sync status for all rules and channels.", tags: ["Admin - Grafana"], }, }, diff --git a/backend/src/api/admin/index.ts b/backend/src/api/admin/index.ts index 1e6724c..589f915 100644 --- a/backend/src/api/admin/index.ts +++ b/backend/src/api/admin/index.ts @@ -5,9 +5,10 @@ import { adminAlerts } from "./alerts"; import { adminApiKey } from "./apiKey"; import { adminCompletions } from "./completions"; import { adminDashboards } from "./dashboards"; -import { adminGrafana } from "./grafana"; import { adminEmbeddings } from "./embeddings"; +import { adminGrafana } from "./grafana"; import { adminModels } from "./models"; +import { adminPlayground } from "./playground"; import { adminProviders } from "./providers"; import { adminRateLimits } from "./rateLimits"; import { adminSearch } from "./search"; @@ -39,6 +40,7 @@ export const routes = new Elysia({ .use(adminSettings) .use(adminDashboards) .use(adminGrafana) + .use(adminPlayground) .get("/", () => true, { detail: { description: "Check whether the admin secret is valid." }, }) diff --git a/backend/src/api/admin/playground.ts b/backend/src/api/admin/playground.ts new file mode 100644 index 0000000..5d8f823 --- /dev/null +++ b/backend/src/api/admin/playground.ts @@ -0,0 +1,444 @@ +import { Elysia, t } from "elysia"; +import { + listPlaygroundConversations, + findPlaygroundConversation, + insertPlaygroundConversation, + updatePlaygroundConversation, + deletePlaygroundConversation, + listPlaygroundMessages, + insertPlaygroundMessage, + deletePlaygroundMessages, + listPlaygroundTestCases, + findPlaygroundTestCase, + insertPlaygroundTestCase, + updatePlaygroundTestCase, + deletePlaygroundTestCase, + listPlaygroundTestRuns, + findPlaygroundTestRun, + insertPlaygroundTestRunWithResults, + deletePlaygroundTestRun, + listPlaygroundTestResults, + updatePlaygroundTestResult, +} from "@/db"; + +const roleSchema = t.Union([ + t.Literal("system"), + t.Literal("user"), + t.Literal("assistant"), +]); + +const paramsSchema = t.Optional( + t.Object({ + systemPrompt: t.Optional(t.String()), + temperature: t.Optional(t.Number({ minimum: 0, maximum: 2 })), + topP: t.Optional(t.Number({ minimum: 0, maximum: 1 })), + topK: t.Optional(t.Number({ minimum: 0 })), + maxTokens: t.Optional(t.Number({ minimum: 1 })), + stopSequences: t.Optional(t.Array(t.String())), + frequencyPenalty: t.Optional(t.Number({ minimum: -2, maximum: 2 })), + presencePenalty: t.Optional(t.Number({ minimum: -2, maximum: 2 })), + }), +); + +// ============================================ +// Conversation Routes +// ============================================ + +const conversationRoutes = new Elysia({ prefix: "/conversations" }) + .get( + "/", + async ({ query }) => { + const offset = query.offset ?? 0; + const limit = query.limit ?? 50; + return await listPlaygroundConversations(offset, limit); + }, + { + query: t.Object({ + offset: t.Optional(t.Numeric()), + limit: t.Optional(t.Numeric({ maximum: 200 })), + }), + detail: { + description: "List playground conversations", + tags: ["Admin - Playground"], + }, + }, + ) + .get( + "/:id", + async ({ params: { id }, status }) => { + const conv = await findPlaygroundConversation(id); + if (!conv) { + return status(404, { error: "Conversation not found" }); + } + const messages = await listPlaygroundMessages(id); + return { ...conv, messages }; + }, + { + params: t.Object({ id: t.Numeric() }), + detail: { + description: "Get conversation with messages", + tags: ["Admin - Playground"], + }, + }, + ) + .post( + "/", + async ({ body, status }) => { + const conv = await insertPlaygroundConversation(body); + if (!conv) { + return status(500, { error: "Failed to create conversation" }); + } + return conv; + }, + { + body: t.Object({ + title: t.String({ minLength: 1, maxLength: 255 }), + model: t.String({ minLength: 1, maxLength: 63 }), + apiKeyId: t.Optional(t.Number()), + params: paramsSchema, + }), + detail: { + description: "Create conversation", + tags: ["Admin - Playground"], + }, + }, + ) + .put( + "/:id", + async ({ params: { id }, body, status }) => { + const existing = await findPlaygroundConversation(id); + if (!existing) { + return status(404, { error: "Conversation not found" }); + } + return await updatePlaygroundConversation(id, body); + }, + { + params: t.Object({ id: t.Numeric() }), + body: t.Object({ + title: t.Optional(t.String({ minLength: 1, maxLength: 255 })), + model: t.Optional(t.String({ minLength: 1, maxLength: 63 })), + apiKeyId: t.Optional(t.Union([t.Number(), t.Null()])), + params: paramsSchema, + }), + detail: { + description: "Update conversation", + tags: ["Admin - Playground"], + }, + }, + ) + .delete( + "/:id", + async ({ params: { id }, status }) => { + const conv = await deletePlaygroundConversation(id); + if (!conv) { + return status(404, { error: "Conversation not found" }); + } + return { success: true }; + }, + { + params: t.Object({ id: t.Numeric() }), + detail: { + description: "Delete conversation (soft delete)", + tags: ["Admin - Playground"], + }, + }, + ) + .post( + "/:id/messages", + async ({ params: { id }, body, status }) => { + const conv = await findPlaygroundConversation(id); + if (!conv) { + return status(404, { error: "Conversation not found" }); + } + const msg = await insertPlaygroundMessage({ + conversationId: id, + ...body, + }); + if (!msg) { + return status(500, { error: "Failed to create message" }); + } + return msg; + }, + { + params: t.Object({ id: t.Numeric() }), + body: t.Object({ + role: roleSchema, + content: t.String(), + completionId: t.Optional(t.Number()), + }), + detail: { + description: "Add message to conversation", + tags: ["Admin - Playground"], + }, + }, + ) + .delete( + "/:id/messages", + async ({ params: { id }, status }) => { + const conv = await findPlaygroundConversation(id); + if (!conv) { + return status(404, { error: "Conversation not found" }); + } + await deletePlaygroundMessages(id); + return { success: true }; + }, + { + params: t.Object({ id: t.Numeric() }), + detail: { + description: "Clear all messages in conversation", + tags: ["Admin - Playground"], + }, + }, + ); + +// ============================================ +// Test Case Routes +// ============================================ + +const testCaseRoutes = new Elysia({ prefix: "/test-cases" }) + .get( + "/", + async ({ query }) => { + const offset = query.offset ?? 0; + const limit = query.limit ?? 50; + return await listPlaygroundTestCases(offset, limit); + }, + { + query: t.Object({ + offset: t.Optional(t.Numeric()), + limit: t.Optional(t.Numeric({ maximum: 200 })), + }), + detail: { + description: "List test cases", + tags: ["Admin - Playground"], + }, + }, + ) + .get( + "/:id", + async ({ params: { id }, status }) => { + const tc = await findPlaygroundTestCase(id); + if (!tc) { + return status(404, { error: "Test case not found" }); + } + return tc; + }, + { + params: t.Object({ id: t.Numeric() }), + detail: { + description: "Get test case", + tags: ["Admin - Playground"], + }, + }, + ) + .post( + "/", + async ({ body, status }) => { + const tc = await insertPlaygroundTestCase(body); + if (!tc) { + return status(500, { error: "Failed to create test case" }); + } + return tc; + }, + { + body: t.Object({ + title: t.String({ minLength: 1, maxLength: 255 }), + description: t.Optional(t.String()), + messages: t.Array( + t.Object({ + role: roleSchema, + content: t.String(), + }), + ), + params: paramsSchema, + }), + detail: { + description: "Create test case", + tags: ["Admin - Playground"], + }, + }, + ) + .put( + "/:id", + async ({ params: { id }, body, status }) => { + const existing = await findPlaygroundTestCase(id); + if (!existing) { + return status(404, { error: "Test case not found" }); + } + return await updatePlaygroundTestCase(id, body); + }, + { + params: t.Object({ id: t.Numeric() }), + body: t.Object({ + title: t.Optional(t.String({ minLength: 1, maxLength: 255 })), + description: t.Optional(t.Union([t.String(), t.Null()])), + messages: t.Optional( + t.Array( + t.Object({ + role: roleSchema, + content: t.String(), + }), + ), + ), + params: paramsSchema, + }), + detail: { + description: "Update test case", + tags: ["Admin - Playground"], + }, + }, + ) + .delete( + "/:id", + async ({ params: { id }, status }) => { + const tc = await deletePlaygroundTestCase(id); + if (!tc) { + return status(404, { error: "Test case not found" }); + } + return { success: true }; + }, + { + params: t.Object({ id: t.Numeric() }), + detail: { + description: "Delete test case (soft delete)", + tags: ["Admin - Playground"], + }, + }, + ); + +// ============================================ +// Test Run Routes +// ============================================ + +const testRunRoutes = new Elysia({ prefix: "/test-runs" }) + .get( + "/", + async ({ query }) => { + const offset = query.offset ?? 0; + const limit = query.limit ?? 50; + return await listPlaygroundTestRuns(offset, limit, query.testCaseId); + }, + { + query: t.Object({ + offset: t.Optional(t.Numeric()), + limit: t.Optional(t.Numeric({ maximum: 200 })), + testCaseId: t.Optional(t.Numeric()), + }), + detail: { + description: "List test runs", + tags: ["Admin - Playground"], + }, + }, + ) + .get( + "/:id", + async ({ params: { id }, status }) => { + const run = await findPlaygroundTestRun(id); + if (!run) { + return status(404, { error: "Test run not found" }); + } + const results = await listPlaygroundTestResults(id); + return { ...run, results }; + }, + { + params: t.Object({ id: t.Numeric() }), + detail: { + description: "Get test run with results", + tags: ["Admin - Playground"], + }, + }, + ) + .post( + "/", + async ({ body, status }) => { + const tc = await findPlaygroundTestCase(body.testCaseId); + if (!tc) { + return status(404, { error: "Test case not found" }); + } + const result = await insertPlaygroundTestRunWithResults( + body, + body.models, + ); + if (!result) { + return status(500, { error: "Failed to create test run" }); + } + return { ...result.run, results: result.results }; + }, + { + body: t.Object({ + testCaseId: t.Number(), + apiKeyId: t.Optional(t.Number()), + models: t.Array(t.String(), { minItems: 1 }), + }), + detail: { + description: "Create test run with pending results", + tags: ["Admin - Playground"], + }, + }, + ) + .delete( + "/:id", + async ({ params: { id }, status }) => { + const run = await deletePlaygroundTestRun(id); + if (!run) { + return status(404, { error: "Test run not found" }); + } + return { success: true }; + }, + { + params: t.Object({ id: t.Numeric() }), + detail: { + description: "Delete test run (soft delete)", + tags: ["Admin - Playground"], + }, + }, + ); + +// ============================================ +// Test Result Routes +// ============================================ + +const testResultRoutes = new Elysia({ prefix: "/test-results" }).put( + "/:id", + async ({ params: { id }, body, status }) => { + const result = await updatePlaygroundTestResult(id, body); + if (!result) { + return status(404, { error: "Test result not found" }); + } + return result; + }, + { + params: t.Object({ id: t.Numeric() }), + body: t.Object({ + status: t.Optional( + t.Union([ + t.Literal("pending"), + t.Literal("running"), + t.Literal("completed"), + t.Literal("failed"), + ]), + ), + response: t.Optional(t.Union([t.String(), t.Null()])), + promptTokens: t.Optional(t.Number()), + completionTokens: t.Optional(t.Number()), + ttft: t.Optional(t.Number()), + duration: t.Optional(t.Number()), + errorMessage: t.Optional(t.Union([t.String(), t.Null()])), + completionId: t.Optional(t.Number()), + }), + detail: { + description: "Update test result", + tags: ["Admin - Playground"], + }, + }, +); + +// ============================================ +// Combine all playground routes +// ============================================ + +export const adminPlayground = new Elysia({ prefix: "/playground" }) + .use(conversationRoutes) + .use(testCaseRoutes) + .use(testRunRoutes) + .use(testResultRoutes); diff --git a/backend/src/api/admin/providers.ts b/backend/src/api/admin/providers.ts index 1808926..fb43d15 100644 --- a/backend/src/api/admin/providers.ts +++ b/backend/src/api/admin/providers.ts @@ -1,7 +1,6 @@ import { Elysia, t } from "elysia"; import { OpenAI } from "openai"; import type { ProviderTypeEnumType } from "@/db/schema"; -import { proxyFetch, getProviderProxy } from "@/utils/proxy-fetch"; import { deleteProvider, findProvider, @@ -10,6 +9,7 @@ import { updateProvider, listModelsByProvider, } from "@/db"; +import { proxyFetch, getProviderProxy } from "@/utils/proxy-fetch"; // ============================================ // Provider Test Strategy Pattern diff --git a/backend/src/api/admin/search.ts b/backend/src/api/admin/search.ts index 0c2df51..1e2e6bb 100644 --- a/backend/src/api/admin/search.ts +++ b/backend/src/api/admin/search.ts @@ -1,15 +1,11 @@ import { Elysia, t } from "elysia"; -import { - parseKql, - compileSearch, - getSearchableFields, -} from "@/search"; import { searchCompletions, aggregateCompletions, searchCompletionsTimeSeries, getDistinctFieldValues, } from "@/db"; +import { parseKql, compileSearch, getSearchableFields } from "@/search"; import { createLogger } from "@/utils/logger"; const logger = createLogger("search"); @@ -36,8 +32,16 @@ function escapeCsvField(value: unknown): string { if (value == null) { return ""; } - const str = typeof value === "object" ? JSON.stringify(value) : String(value as string | number | boolean); - if (str.includes(",") || str.includes("\n") || str.includes("\r") || str.includes('"')) { + const str = + typeof value === "object" + ? JSON.stringify(value) + : String(value as string | number | boolean); + if ( + str.includes(",") || + str.includes("\n") || + str.includes("\r") || + str.includes('"') + ) { return `"${str.replace(/"/g, '""')}"`; } return str; @@ -60,7 +64,9 @@ export const adminSearch = new Elysia() try { timeRange = parseTimeRange(body.timeRange?.from, body.timeRange?.to); } catch (err) { - return status(400, { error: err instanceof Error ? err.message : "Invalid timeRange" }); + return status(400, { + error: err instanceof Error ? err.message : "Invalid timeRange", + }); } let compiled; try { @@ -136,7 +142,9 @@ export const adminSearch = new Elysia() try { timeRange = parseTimeRange(body.timeRange?.from, body.timeRange?.to); } catch (err) { - return status(400, { error: err instanceof Error ? err.message : "Invalid timeRange" }); + return status(400, { + error: err instanceof Error ? err.message : "Invalid timeRange", + }); } let compiled; try { @@ -206,7 +214,9 @@ export const adminSearch = new Elysia() try { timeRange = parseTimeRange(body.timeRange?.from, body.timeRange?.to); } catch (err) { - return status(400, { error: err instanceof Error ? err.message : "Invalid timeRange" }); + return status(400, { + error: err instanceof Error ? err.message : "Invalid timeRange", + }); } let compiled; try { diff --git a/backend/src/api/v1/completions.ts b/backend/src/api/v1/completions.ts index 24f5281..f25cb5b 100644 --- a/backend/src/api/v1/completions.ts +++ b/backend/src/api/v1/completions.ts @@ -5,7 +5,6 @@ import { Elysia, t } from "elysia"; import type { ModelWithProvider } from "@/adapters/types"; -import { getProviderProxy } from "@/utils/proxy-fetch"; import type { CompletionsMessageType, ToolDefinitionType, @@ -40,6 +39,7 @@ import { } from "@/utils/api-helpers"; import { addCompletions, type Completion } from "@/utils/completions"; import { createLogger } from "@/utils/logger"; +import { getProviderProxy } from "@/utils/proxy-fetch"; import { checkReqId, finalizeReqId, diff --git a/backend/src/api/v1/embeddings.ts b/backend/src/api/v1/embeddings.ts index 147ffb1..07c7859 100644 --- a/backend/src/api/v1/embeddings.ts +++ b/backend/src/api/v1/embeddings.ts @@ -123,7 +123,11 @@ export const embeddingsApi = new Elysia({ // Make request to upstream const proxy = getProviderProxy(provider); - const [resp, fetchError] = await proxyFetch(upstreamEndpoint, reqInit, proxy) + const [resp, fetchError] = await proxyFetch( + upstreamEndpoint, + reqInit, + proxy, + ) .then((r) => [r, null] as [Response, null]) .catch((err: unknown) => { logger.error("fetch error", err); diff --git a/backend/src/api/v1/messages.ts b/backend/src/api/v1/messages.ts index 63a924d..c6237ba 100644 --- a/backend/src/api/v1/messages.ts +++ b/backend/src/api/v1/messages.ts @@ -6,7 +6,6 @@ import type { TProperties } from "@sinclair/typebox"; import { Elysia, t } from "elysia"; import type { ModelWithProvider } from "@/adapters/types"; -import { getProviderProxy } from "@/utils/proxy-fetch"; import type { CachedResponseType } from "@/db/schema"; import { getRequestAdapter, @@ -36,6 +35,7 @@ import { import { addCompletions, type Completion } from "@/utils/completions"; import { safeParseToolArgs } from "@/utils/json"; import { createLogger } from "@/utils/logger"; +import { getProviderProxy } from "@/utils/proxy-fetch"; import { checkReqId, finalizeReqId, diff --git a/backend/src/api/v1/responses.ts b/backend/src/api/v1/responses.ts index 0bf8a33..67a74a6 100644 --- a/backend/src/api/v1/responses.ts +++ b/backend/src/api/v1/responses.ts @@ -5,7 +5,6 @@ import { Elysia, t } from "elysia"; import type { ModelWithProvider } from "@/adapters/types"; -import { getProviderProxy } from "@/utils/proxy-fetch"; import type { CachedResponseType } from "@/db/schema"; import { getRequestAdapter, @@ -34,6 +33,7 @@ import { } from "@/utils/api-helpers"; import { addCompletions, type Completion } from "@/utils/completions"; import { createLogger } from "@/utils/logger"; +import { getProviderProxy } from "@/utils/proxy-fetch"; import { checkReqId, finalizeReqId, diff --git a/backend/src/db/index.ts b/backend/src/db/index.ts index 721cea3..1db6744 100644 --- a/backend/src/db/index.ts +++ b/backend/src/db/index.ts @@ -1896,7 +1896,12 @@ export async function getCompletionLatencyPercentile( percentile: number, model?: string, ): Promise { - logger.debug("getCompletionLatencyPercentile", windowMinutes, percentile, model); + logger.debug( + "getCompletionLatencyPercentile", + windowMinutes, + percentile, + model, + ); const pValue = percentile / 100; const result = await db.execute(sql` SELECT COALESCE( @@ -2101,7 +2106,11 @@ export async function searchCompletionsTimeSeries( failed: string; }[] > { - logger.debug("searchCompletionsTimeSeries", compiled.whereClause, bucketSeconds); + logger.debug( + "searchCompletionsTimeSeries", + compiled.whereClause, + bucketSeconds, + ); const whereSql = buildDrizzleSql(compiled.whereClause, compiled.params); @@ -2156,3 +2165,381 @@ export async function getDistinctFieldValues( return (result as unknown as { value: string }[]).map((r) => r.value); } + +// ============================================ +// Playground Types +// ============================================ + +export type PlaygroundConversation = + typeof schema.PlaygroundConversationsTable.$inferSelect; +export type PlaygroundConversationInsert = + typeof schema.PlaygroundConversationsTable.$inferInsert; +export type PlaygroundMessage = + typeof schema.PlaygroundMessagesTable.$inferSelect; +export type PlaygroundMessageInsert = + typeof schema.PlaygroundMessagesTable.$inferInsert; +export type PlaygroundTestCase = + typeof schema.PlaygroundTestCasesTable.$inferSelect; +export type PlaygroundTestCaseInsert = + typeof schema.PlaygroundTestCasesTable.$inferInsert; +export type PlaygroundTestRun = + typeof schema.PlaygroundTestRunsTable.$inferSelect; +export type PlaygroundTestRunInsert = + typeof schema.PlaygroundTestRunsTable.$inferInsert; +export type PlaygroundTestResult = + typeof schema.PlaygroundTestResultsTable.$inferSelect; +export type PlaygroundTestResultInsert = + typeof schema.PlaygroundTestResultsTable.$inferInsert; + +// ============================================ +// Playground Conversation CRUD +// ============================================ + +export async function listPlaygroundConversations( + offset: number, + limit: number, +): Promise> { + logger.debug("listPlaygroundConversations", offset, limit); + const r = await db + .select() + .from(schema.PlaygroundConversationsTable) + .where(not(schema.PlaygroundConversationsTable.deleted)) + .orderBy(desc(schema.PlaygroundConversationsTable.updatedAt)) + .offset(offset) + .limit(limit); + const [total] = await db + .select({ total: count(schema.PlaygroundConversationsTable.id) }) + .from(schema.PlaygroundConversationsTable) + .where(not(schema.PlaygroundConversationsTable.deleted)); + if (!total) throw new Error("total count failed"); + return { data: r, total: total.total, from: offset }; +} + +export async function findPlaygroundConversation( + id: number, +): Promise { + logger.debug("findPlaygroundConversation", id); + const r = await db + .select() + .from(schema.PlaygroundConversationsTable) + .where( + and( + eq(schema.PlaygroundConversationsTable.id, id), + not(schema.PlaygroundConversationsTable.deleted), + ), + ); + const [first] = r; + return first ?? null; +} + +export async function insertPlaygroundConversation( + c: PlaygroundConversationInsert, +): Promise { + logger.debug("insertPlaygroundConversation", c.title); + const r = await db + .insert(schema.PlaygroundConversationsTable) + .values(c) + .returning(); + const [first] = r; + return first ?? null; +} + +export async function updatePlaygroundConversation( + id: number, + c: Partial, +): Promise { + logger.debug("updatePlaygroundConversation", id); + const r = await db + .update(schema.PlaygroundConversationsTable) + .set({ ...c, updatedAt: new Date() }) + .where(eq(schema.PlaygroundConversationsTable.id, id)) + .returning(); + const [first] = r; + return first ?? null; +} + +export async function deletePlaygroundConversation( + id: number, +): Promise { + logger.debug("deletePlaygroundConversation", id); + const r = await db + .update(schema.PlaygroundConversationsTable) + .set({ deleted: true, updatedAt: new Date() }) + .where(eq(schema.PlaygroundConversationsTable.id, id)) + .returning(); + const [first] = r; + return first ?? null; +} + +// ============================================ +// Playground Message CRUD +// ============================================ + +export async function listPlaygroundMessages( + conversationId: number, +): Promise { + logger.debug("listPlaygroundMessages", conversationId); + return await db + .select() + .from(schema.PlaygroundMessagesTable) + .where(eq(schema.PlaygroundMessagesTable.conversationId, conversationId)) + .orderBy(asc(schema.PlaygroundMessagesTable.id)); +} + +export async function insertPlaygroundMessage( + m: PlaygroundMessageInsert, +): Promise { + logger.debug("insertPlaygroundMessage", m.conversationId, m.role); + const r = await db + .insert(schema.PlaygroundMessagesTable) + .values(m) + .returning(); + const [first] = r; + // Touch conversation updatedAt + if (first) { + await db + .update(schema.PlaygroundConversationsTable) + .set({ updatedAt: new Date() }) + .where(eq(schema.PlaygroundConversationsTable.id, m.conversationId)); + } + return first ?? null; +} + +export async function deletePlaygroundMessages( + conversationId: number, +): Promise { + logger.debug("deletePlaygroundMessages", conversationId); + await db + .delete(schema.PlaygroundMessagesTable) + .where(eq(schema.PlaygroundMessagesTable.conversationId, conversationId)); +} + +// ============================================ +// Playground Test Case CRUD +// ============================================ + +export async function listPlaygroundTestCases( + offset: number, + limit: number, +): Promise> { + logger.debug("listPlaygroundTestCases", offset, limit); + const r = await db + .select() + .from(schema.PlaygroundTestCasesTable) + .where(not(schema.PlaygroundTestCasesTable.deleted)) + .orderBy(desc(schema.PlaygroundTestCasesTable.updatedAt)) + .offset(offset) + .limit(limit); + const [total] = await db + .select({ total: count(schema.PlaygroundTestCasesTable.id) }) + .from(schema.PlaygroundTestCasesTable) + .where(not(schema.PlaygroundTestCasesTable.deleted)); + if (!total) throw new Error("total count failed"); + return { data: r, total: total.total, from: offset }; +} + +export async function findPlaygroundTestCase( + id: number, +): Promise { + logger.debug("findPlaygroundTestCase", id); + const r = await db + .select() + .from(schema.PlaygroundTestCasesTable) + .where( + and( + eq(schema.PlaygroundTestCasesTable.id, id), + not(schema.PlaygroundTestCasesTable.deleted), + ), + ); + const [first] = r; + return first ?? null; +} + +export async function insertPlaygroundTestCase( + c: PlaygroundTestCaseInsert, +): Promise { + logger.debug("insertPlaygroundTestCase", c.title); + const r = await db + .insert(schema.PlaygroundTestCasesTable) + .values(c) + .returning(); + const [first] = r; + return first ?? null; +} + +export async function updatePlaygroundTestCase( + id: number, + c: Partial, +): Promise { + logger.debug("updatePlaygroundTestCase", id); + const r = await db + .update(schema.PlaygroundTestCasesTable) + .set({ ...c, updatedAt: new Date() }) + .where(eq(schema.PlaygroundTestCasesTable.id, id)) + .returning(); + const [first] = r; + return first ?? null; +} + +export async function deletePlaygroundTestCase( + id: number, +): Promise { + logger.debug("deletePlaygroundTestCase", id); + const r = await db + .update(schema.PlaygroundTestCasesTable) + .set({ deleted: true, updatedAt: new Date() }) + .where(eq(schema.PlaygroundTestCasesTable.id, id)) + .returning(); + const [first] = r; + return first ?? null; +} + +// ============================================ +// Playground Test Run CRUD +// ============================================ + +export async function listPlaygroundTestRuns( + offset: number, + limit: number, + testCaseId?: number, +): Promise> { + logger.debug("listPlaygroundTestRuns", offset, limit, testCaseId); + const r = await db + .select() + .from(schema.PlaygroundTestRunsTable) + .where( + and( + not(schema.PlaygroundTestRunsTable.deleted), + testCaseId !== undefined + ? eq(schema.PlaygroundTestRunsTable.testCaseId, testCaseId) + : undefined, + ), + ) + .orderBy(desc(schema.PlaygroundTestRunsTable.createdAt)) + .offset(offset) + .limit(limit); + const [total] = await db + .select({ total: count(schema.PlaygroundTestRunsTable.id) }) + .from(schema.PlaygroundTestRunsTable) + .where( + and( + not(schema.PlaygroundTestRunsTable.deleted), + testCaseId !== undefined + ? eq(schema.PlaygroundTestRunsTable.testCaseId, testCaseId) + : undefined, + ), + ); + if (!total) throw new Error("total count failed"); + return { data: r, total: total.total, from: offset }; +} + +export async function findPlaygroundTestRun( + id: number, +): Promise { + logger.debug("findPlaygroundTestRun", id); + const r = await db + .select() + .from(schema.PlaygroundTestRunsTable) + .where( + and( + eq(schema.PlaygroundTestRunsTable.id, id), + not(schema.PlaygroundTestRunsTable.deleted), + ), + ); + const [first] = r; + return first ?? null; +} + +export async function insertPlaygroundTestRun( + c: PlaygroundTestRunInsert, +): Promise { + logger.debug("insertPlaygroundTestRun", c.testCaseId); + const r = await db + .insert(schema.PlaygroundTestRunsTable) + .values(c) + .returning(); + const [first] = r; + return first ?? null; +} + +export async function insertPlaygroundTestRunWithResults( + c: PlaygroundTestRunInsert, + models: string[], +): Promise<{ run: PlaygroundTestRun; results: PlaygroundTestResult[] } | null> { + logger.debug("insertPlaygroundTestRunWithResults", c.testCaseId, models); + return await db.transaction(async (tx) => { + const [run] = await tx + .insert(schema.PlaygroundTestRunsTable) + .values(c) + .returning(); + if (!run) return null; + + const results: PlaygroundTestResult[] = []; + for (const model of models) { + const [result] = await tx + .insert(schema.PlaygroundTestResultsTable) + .values({ testRunId: run.id, model, status: "pending" }) + .returning(); + if (!result) { + throw new Error(`Failed to insert test result for model: ${model}`); + } + results.push(result); + } + + return { run, results }; + }); +} + +export async function deletePlaygroundTestRun( + id: number, +): Promise { + logger.debug("deletePlaygroundTestRun", id); + const r = await db + .update(schema.PlaygroundTestRunsTable) + .set({ deleted: true }) + .where(eq(schema.PlaygroundTestRunsTable.id, id)) + .returning(); + const [first] = r; + return first ?? null; +} + +// ============================================ +// Playground Test Result CRUD +// ============================================ + +export async function listPlaygroundTestResults( + testRunId: number, +): Promise { + logger.debug("listPlaygroundTestResults", testRunId); + return await db + .select() + .from(schema.PlaygroundTestResultsTable) + .where(eq(schema.PlaygroundTestResultsTable.testRunId, testRunId)) + .orderBy(asc(schema.PlaygroundTestResultsTable.id)); +} + +export async function insertPlaygroundTestResult( + r: PlaygroundTestResultInsert, +): Promise { + logger.debug("insertPlaygroundTestResult", r.testRunId, r.model); + const result = await db + .insert(schema.PlaygroundTestResultsTable) + .values(r) + .returning(); + const [first] = result; + return first ?? null; +} + +export async function updatePlaygroundTestResult( + id: number, + r: Partial, +): Promise { + logger.debug("updatePlaygroundTestResult", id); + const result = await db + .update(schema.PlaygroundTestResultsTable) + .set({ ...r, updatedAt: new Date() }) + .where(eq(schema.PlaygroundTestResultsTable.id, id)) + .returning(); + const [first] = result; + return first ?? null; +} diff --git a/backend/src/db/schema.ts b/backend/src/db/schema.ts index cdbc49a..6fc8f14 100644 --- a/backend/src/db/schema.ts +++ b/backend/src/db/schema.ts @@ -484,3 +484,117 @@ export const AlertHistoryTable = pgTable("alert_history", { payload: jsonb("payload").notNull().$type(), status: AlertHistoryStatusEnum("status").notNull(), }); + +// ============================================ +// Playground Tables +// ============================================ + +/** + * Playground model parameters (shared JSONB type for conversations and test cases) + */ +export type PlaygroundParamsType = { + systemPrompt?: string; + temperature?: number; + topP?: number; + topK?: number; + maxTokens?: number; + stopSequences?: string[]; + frequencyPenalty?: number; + presencePenalty?: number; +}; + +export const PlaygroundMessageRoleEnum = pgEnum("playground_message_role", [ + "system", + "user", + "assistant", +]); + +export const PlaygroundTestResultStatusEnum = pgEnum( + "playground_test_result_status", + ["pending", "running", "completed", "failed"], +); + +export const PlaygroundConversationsTable = pgTable( + "playground_conversations", + { + id: integer("id").primaryKey().generatedAlwaysAsIdentity(), + title: varchar("title", { length: 255 }).notNull(), + model: varchar("model", { length: 63 }).notNull(), + apiKeyId: integer("api_key_id").references( + (): AnyPgColumn => ApiKeysTable.id, + { onDelete: "set null" }, + ), + params: jsonb("params").$type(), + createdAt: timestamp("created_at").notNull().defaultNow(), + updatedAt: timestamp("updated_at").notNull().defaultNow(), + deleted: boolean("deleted").notNull().default(false), + }, +); + +export const PlaygroundMessagesTable = pgTable("playground_messages", { + id: integer("id").primaryKey().generatedAlwaysAsIdentity(), + conversationId: integer("conversation_id") + .notNull() + .references((): AnyPgColumn => PlaygroundConversationsTable.id, { + onDelete: "cascade", + }), + role: PlaygroundMessageRoleEnum("role").notNull(), + content: varchar("content").notNull(), + completionId: integer("completion_id").references( + (): AnyPgColumn => CompletionsTable.id, + { onDelete: "set null" }, + ), + createdAt: timestamp("created_at").notNull().defaultNow(), +}); + +export const PlaygroundTestCasesTable = pgTable("playground_test_cases", { + id: integer("id").primaryKey().generatedAlwaysAsIdentity(), + title: varchar("title", { length: 255 }).notNull(), + description: varchar("description"), + messages: jsonb("messages") + .notNull() + .$type<{ role: string; content: string }[]>(), + params: jsonb("params").$type(), + createdAt: timestamp("created_at").notNull().defaultNow(), + updatedAt: timestamp("updated_at").notNull().defaultNow(), + deleted: boolean("deleted").notNull().default(false), +}); + +export const PlaygroundTestRunsTable = pgTable("playground_test_runs", { + id: integer("id").primaryKey().generatedAlwaysAsIdentity(), + testCaseId: integer("test_case_id") + .notNull() + .references((): AnyPgColumn => PlaygroundTestCasesTable.id, { + onDelete: "cascade", + }), + apiKeyId: integer("api_key_id").references( + (): AnyPgColumn => ApiKeysTable.id, + { onDelete: "set null" }, + ), + models: jsonb("models").notNull().$type(), + createdAt: timestamp("created_at").notNull().defaultNow(), + deleted: boolean("deleted").notNull().default(false), +}); + +export const PlaygroundTestResultsTable = pgTable("playground_test_results", { + id: integer("id").primaryKey().generatedAlwaysAsIdentity(), + testRunId: integer("test_run_id") + .notNull() + .references((): AnyPgColumn => PlaygroundTestRunsTable.id, { + onDelete: "cascade", + }), + model: varchar("model", { length: 63 }).notNull(), + status: PlaygroundTestResultStatusEnum("status").notNull().default("pending"), + response: varchar("response"), + promptTokens: integer("prompt_tokens"), + completionTokens: integer("completion_tokens"), + ttft: integer("ttft"), + duration: integer("duration"), + errorMessage: varchar("error_message"), + completionId: integer("completion_id").references( + (): AnyPgColumn => CompletionsTable.id, + { onDelete: "set null" }, + ), + createdAt: timestamp("created_at").notNull().defaultNow(), + updatedAt: timestamp("updated_at").notNull().defaultNow(), +}); diff --git a/backend/src/search/__tests__/compiler.test.ts b/backend/src/search/__tests__/compiler.test.ts index 693e47f..2752531 100644 --- a/backend/src/search/__tests__/compiler.test.ts +++ b/backend/src/search/__tests__/compiler.test.ts @@ -1,8 +1,11 @@ import { describe, expect, test } from "bun:test"; -import { parseKql } from "../parser"; import { compileSearch } from "../compiler"; +import { parseKql } from "../parser"; -function compile(input: string, options?: { timeRange?: { from: Date; to: Date } }) { +function compile( + input: string, + options?: { timeRange?: { from: Date; to: Date } }, +) { const result = parseKql(input); if (!result.success) { throw new Error(`Parse error: ${result.error.message}`); @@ -34,9 +37,7 @@ describe("SQL Compiler", () => { test("status: completed validates enum value", () => { const compiled = compile("status: completed"); - expect(compiled.whereClause).toBe( - "c.deleted = false AND c.status = $1", - ); + expect(compiled.whereClause).toBe("c.deleted = false AND c.status = $1"); expect(compiled.params).toEqual(["completed"]); }); @@ -63,9 +64,7 @@ describe("SQL Compiler", () => { describe("boolean operators", () => { test("AND combines with AND", () => { - const compiled = compile( - 'model: "gpt-4" AND status: completed', - ); + const compiled = compile('model: "gpt-4" AND status: completed'); expect(compiled.whereClause).toBe( "c.deleted = false AND (c.model = $1 AND c.status = $2)", ); @@ -73,9 +72,7 @@ describe("SQL Compiler", () => { }); test("OR combines with OR", () => { - const compiled = compile( - 'status: failed OR status: aborted', - ); + const compiled = compile("status: failed OR status: aborted"); expect(compiled.whereClause).toBe( "c.deleted = false AND (c.status = $1 OR c.status = $2)", ); @@ -92,9 +89,11 @@ describe("SQL Compiler", () => { test("grouped expression", () => { const compiled = compile( - '(status: completed OR status: cache_hit) AND model: *gpt*', + "(status: completed OR status: cache_hit) AND model: *gpt*", + ); + expect(compiled.whereClause).toContain( + "(c.status = $1 OR c.status = $2)", ); - expect(compiled.whereClause).toContain("(c.status = $1 OR c.status = $2)"); expect(compiled.whereClause).toContain("c.model ILIKE $3"); expect(compiled.params).toEqual(["completed", "cache_hit", "%gpt%"]); }); @@ -111,10 +110,8 @@ describe("SQL Compiler", () => { }); describe("JSONB fields", () => { - test('extraHeaders.x-experiment compiles to JSONB path', () => { - const compiled = compile( - 'extraHeaders.x-experiment: "group_a"', - ); + test("extraHeaders.x-experiment compiles to JSONB path", () => { + const compiled = compile('extraHeaders.x-experiment: "group_a"'); expect(compiled.whereClause).toBe( "c.deleted = false AND (c.prompt #>> '{}')::jsonb->'extraHeaders'->>'x-experiment' = $1", ); @@ -208,9 +205,7 @@ describe("SQL Compiler", () => { }); test("multiple aggregations", () => { - const compiled = compile( - "| stats avg(duration), count(), p95(ttft)", - ); + const compiled = compile("| stats avg(duration), count(), p95(ttft)"); expect(compiled.aggregation!.selectExpressions).toHaveLength(3); }); @@ -251,9 +246,7 @@ describe("SQL Compiler", () => { describe("provider field", () => { test("provider compiles to joined p.name", () => { const compiled = compile('provider: "openai"'); - expect(compiled.whereClause).toBe( - "c.deleted = false AND p.name = $1", - ); + expect(compiled.whereClause).toBe("c.deleted = false AND p.name = $1"); expect(compiled.params).toEqual(["openai"]); }); }); @@ -300,12 +293,8 @@ describe("SQL Compiler", () => { }); test("EXISTS combined with other filters", () => { - const compiled = compile( - 'toolCalls EXISTS AND model: "claude-haiku-*"', - ); - expect(compiled.whereClause).toContain( - "jsonb_array_elements", - ); + const compiled = compile('toolCalls EXISTS AND model: "claude-haiku-*"'); + expect(compiled.whereClause).toContain("jsonb_array_elements"); expect(compiled.whereClause).toContain("c.model"); }); }); diff --git a/backend/src/search/__tests__/parser.test.ts b/backend/src/search/__tests__/parser.test.ts index 81d1184..b1e21db 100644 --- a/backend/src/search/__tests__/parser.test.ts +++ b/backend/src/search/__tests__/parser.test.ts @@ -297,7 +297,7 @@ describe("KQL Parser", () => { test("filter with aggregation", () => { const result = parseKql( - 'status: completed AND duration >= 500 | stats avg(duration), p95(ttft), count() by model', + "status: completed AND duration >= 500 | stats avg(duration), p95(ttft), count() by model", ); expect(result.success).toBe(true); if (result.success) { @@ -376,7 +376,7 @@ describe("KQL Parser", () => { }); describe("quoted strings with escapes", () => { - test('escaped quote in string', () => { + test("escaped quote in string", () => { const result = parseKql('model: "gpt-\\"4\\""'); expect(result.success).toBe(true); if (result.success) { diff --git a/backend/src/search/compiler.ts b/backend/src/search/compiler.ts index d3654aa..19b3b4d 100644 --- a/backend/src/search/compiler.ts +++ b/backend/src/search/compiler.ts @@ -264,7 +264,11 @@ class SqlCompiler { } // For JSONB root fields (e.g., "extraBody EXISTS") - if (mapping.type === "jsonb" && mapping.jsonbColumn && mapping.jsonbRootKey) { + if ( + mapping.type === "jsonb" && + mapping.jsonbColumn && + mapping.jsonbRootKey + ) { const unwrapped = unwrapJsonb(mapping.jsonbColumn); // Array-rooted JSONB (e.g., toolCalls — completion is an array of messages) @@ -335,7 +339,9 @@ class SqlCompiler { comparison = `${pathSql} ILIKE ${param}`; } else { const strValue = - expr.value.type === "string" ? expr.value.value : String(expr.value.value); + expr.value.type === "string" + ? expr.value.value + : String(expr.value.value); const param = this.addParam(strValue); if (expr.operator === ":" || expr.operator === "=") { comparison = `${pathSql} = ${param}`; @@ -360,13 +366,19 @@ class SqlCompiler { if (rootField) { const rootMapping = FIELD_REGISTRY[rootField]; if (rootMapping?.jsonbRootIsArray) { - return this.compileArrayJsonbComparison(expr, rootMapping, parts.slice(1)); + return this.compileArrayJsonbComparison( + expr, + rootMapping, + parts.slice(1), + ); } } - const { sql: fieldSql, mapping, isJsonbPath } = this.resolveField( - expr.field, - ); + const { + sql: fieldSql, + mapping, + isJsonbPath, + } = this.resolveField(expr.field); // For JSONB paths, values are always text (extracted with ->>) if (isJsonbPath) { @@ -415,7 +427,8 @@ class SqlCompiler { return `${fieldSql} ILIKE ${param}`; } - const strValue = value.type === "string" ? value.value : String(value.value); + const strValue = + value.type === "string" ? value.value : String(value.value); const param = this.addParam(strValue); if (operator === ":" || operator === "=") { @@ -445,9 +458,7 @@ class SqlCompiler { ); } } else { - throw new CompilerError( - `Wildcards are not supported for numeric fields`, - ); + throw new CompilerError(`Wildcards are not supported for numeric fields`); } const param = this.addParam(numValue); @@ -468,7 +479,8 @@ class SqlCompiler { return `${fieldSql}::text ILIKE ${param}`; } - const strValue = value.type === "string" ? value.value : String(value.value); + const strValue = + value.type === "string" ? value.value : String(value.value); if (validValues.length > 0 && !validValues.includes(strValue)) { throw new CompilerError( @@ -495,10 +507,13 @@ class SqlCompiler { value: KqlValue, ): string { if (value.type === "wildcard") { - throw new CompilerError(`Wildcards are not supported for timestamp fields`); + throw new CompilerError( + `Wildcards are not supported for timestamp fields`, + ); } - const strValue = value.type === "string" ? value.value : String(value.value); + const strValue = + value.type === "string" ? value.value : String(value.value); const param = this.addParam(strValue); const sqlOp = operator === ":" ? "=" : operator; return `${fieldSql} ${sqlOp} ${param}::timestamp`; @@ -516,7 +531,8 @@ class SqlCompiler { return `${fieldSql} ILIKE ${param}`; } - const strValue = value.type === "string" ? value.value : String(value.value); + const strValue = + value.type === "string" ? value.value : String(value.value); const param = this.addParam(strValue); if (operator === ":" || operator === "=") { @@ -719,4 +735,3 @@ export function getSearchableFields(): FieldInfo[] { nested: mapping.nested, })); } - diff --git a/backend/src/search/lexer.ts b/backend/src/search/lexer.ts index ba27fe5..4b36784 100644 --- a/backend/src/search/lexer.ts +++ b/backend/src/search/lexer.ts @@ -119,7 +119,10 @@ export function tokenize(input: string): Token[] { } // Number (integer or decimal, optionally negative) - if (/\d/.test(ch) || (ch === "-" && pos + 1 < input.length && /\d/.test(charAt(pos + 1)))) { + if ( + /\d/.test(ch) || + (ch === "-" && pos + 1 < input.length && /\d/.test(charAt(pos + 1))) + ) { let num = ch; pos++; let hasDot = false; @@ -182,11 +185,7 @@ export function tokenize(input: string): Token[] { continue; } - throw new LexerError( - `Unexpected character '${ch}'`, - pos, - 1, - ); + throw new LexerError(`Unexpected character '${ch}'`, pos, 1); } tokens.push({ type: "EOF", value: "", position: pos }); diff --git a/backend/src/search/parser.ts b/backend/src/search/parser.ts index 9293b69..1b6dd5d 100644 --- a/backend/src/search/parser.ts +++ b/backend/src/search/parser.ts @@ -1,4 +1,3 @@ -import { tokenize, LexerError } from "./lexer"; import type { Token, TokenType, @@ -10,6 +9,7 @@ import type { AggregateFunction, ParseResult, } from "./types"; +import { tokenize, LexerError } from "./lexer"; const AGGREGATE_FUNCTIONS = new Set([ "count", diff --git a/backend/src/search/types.ts b/backend/src/search/types.ts index aafbb93..794aa20 100644 --- a/backend/src/search/types.ts +++ b/backend/src/search/types.ts @@ -33,14 +33,7 @@ export type KqlValue = | { type: "number"; value: number } | { type: "wildcard"; pattern: string }; -export type ComparisonOperator = - | ":" - | "=" - | "!=" - | ">" - | ">=" - | "<" - | "<="; +export type ComparisonOperator = ":" | "=" | "!=" | ">" | ">=" | "<" | "<="; export type KqlExpression = | { @@ -112,12 +105,7 @@ export interface CompiledQuery { // --- Field metadata for autocomplete --- -export type FieldType = - | "text" - | "number" - | "enum" - | "timestamp" - | "jsonb"; +export type FieldType = "text" | "number" | "enum" | "timestamp" | "jsonb"; export interface FieldInfo { /** Field name as used in KQL queries (e.g., "model", "extraHeaders.x-experiment") */ diff --git a/backend/src/services/alertDispatcher.ts b/backend/src/services/alertDispatcher.ts index 1e3af59..797e73a 100644 --- a/backend/src/services/alertDispatcher.ts +++ b/backend/src/services/alertDispatcher.ts @@ -1,5 +1,4 @@ import { createTransport } from "nodemailer"; -import { createLogger } from "@/utils/logger"; import type { AlertChannelConfig, AlertPayload, @@ -8,6 +7,7 @@ import type { WebhookChannelConfig, AlertChannelTypeEnumType, } from "@/db/schema"; +import { createLogger } from "@/utils/logger"; const logger = createLogger("alertDispatcher"); diff --git a/backend/src/services/alertEngine.ts b/backend/src/services/alertEngine.ts index a45a596..48be45e 100644 --- a/backend/src/services/alertEngine.ts +++ b/backend/src/services/alertEngine.ts @@ -1,5 +1,10 @@ -import { createLogger } from "@/utils/logger"; -import { redisClient } from "@/utils/redisClient"; +import type { + AlertPayload, + BudgetCondition, + ErrorRateCondition, + LatencyCondition, + QuotaCondition, +} from "@/db/schema"; import { listAlertRules, listAlertChannels, @@ -11,14 +16,9 @@ import { type AlertRule, type AlertChannel, } from "@/db"; -import type { - AlertPayload, - BudgetCondition, - ErrorRateCondition, - LatencyCondition, - QuotaCondition, -} from "@/db/schema"; import { getRateLimitStatus } from "@/utils/apiKeyRateLimit"; +import { createLogger } from "@/utils/logger"; +import { redisClient } from "@/utils/redisClient"; import { dispatchToChannel } from "./alertDispatcher"; import { isGrafanaConnected } from "./grafanaSync"; @@ -194,10 +194,7 @@ async function evaluateQuota( /** * Build alert payload based on rule type and evaluation result */ -function buildPayload( - rule: AlertRule, - currentValue: number, -): AlertPayload { +function buildPayload(rule: AlertRule, currentValue: number): AlertPayload { const condition = rule.condition; let threshold: number; let message: string; @@ -249,9 +246,7 @@ async function dispatchAlert( channels: AlertChannel[], payload: AlertPayload, ): Promise { - const ruleChannels = channels.filter((ch) => - rule.channelIds.includes(ch.id), - ); + const ruleChannels = channels.filter((ch) => rule.channelIds.includes(ch.id)); for (const channel of ruleChannels) { if (!channel.enabled) { @@ -356,9 +351,7 @@ export function startAlertEngine(): void { return; } - logger.info( - `Starting alert engine (interval: ${ALERT_CHECK_INTERVAL_MS}ms)`, - ); + logger.info(`Starting alert engine (interval: ${ALERT_CHECK_INTERVAL_MS}ms)`); intervalId = setInterval(() => { void evaluateAlerts(); }, ALERT_CHECK_INTERVAL_MS); diff --git a/backend/src/services/failover.ts b/backend/src/services/failover.ts index 1ab825f..59e3ab6 100644 --- a/backend/src/services/failover.ts +++ b/backend/src/services/failover.ts @@ -236,7 +236,12 @@ export async function executeWithFailover( url, }); - const response = await fetchWithTimeout(url, init, cfg.timeoutMs, proxy); + const response = await fetchWithTimeout( + url, + init, + cfg.timeoutMs, + proxy, + ); // Success - return immediately if (response.ok) { diff --git a/backend/src/services/grafanaSync.ts b/backend/src/services/grafanaSync.ts index 6e5e9d3..9fb1403 100644 --- a/backend/src/services/grafanaSync.ts +++ b/backend/src/services/grafanaSync.ts @@ -1,18 +1,3 @@ -import { createLogger } from "@/utils/logger"; -import { - GrafanaClient, - type GrafanaAlertRulePayload, - type GrafanaContactPointPayload, -} from "@/utils/grafanaClient"; -import { - listAlertRules, - listAlertChannels, - getSetting, - updateAlertRuleGrafanaSync, - updateAlertChannelGrafanaSync, - type AlertRule, - type AlertChannel, -} from "@/db"; import type { BudgetCondition, ErrorRateCondition, @@ -22,6 +7,21 @@ import type { EmailChannelConfig, FeishuChannelConfig, } from "@/db/schema"; +import { + listAlertRules, + listAlertChannels, + getSetting, + updateAlertRuleGrafanaSync, + updateAlertChannelGrafanaSync, + type AlertRule, + type AlertChannel, +} from "@/db"; +import { + GrafanaClient, + type GrafanaAlertRulePayload, + type GrafanaContactPointPayload, +} from "@/utils/grafanaClient"; +import { createLogger } from "@/utils/logger"; const logger = createLogger("grafanaSync"); @@ -200,9 +200,7 @@ function buildGrafanaAlertRule( // Channel to Contact Point Mapping // ============================================ -function buildContactPoint( - channel: AlertChannel, -): GrafanaContactPointPayload { +function buildContactPoint(channel: AlertChannel): GrafanaContactPointPayload { switch (channel.type) { case "webhook": { const c = channel.config as WebhookChannelConfig; @@ -284,8 +282,7 @@ export async function syncRulesToGrafana(): Promise { result.synced++; } catch (error) { - const errorMsg = - error instanceof Error ? error.message : String(error); + const errorMsg = error instanceof Error ? error.message : String(error); await updateAlertRuleGrafanaSync(rule.id, { grafanaSyncError: errorMsg.slice(0, 500), }); @@ -334,8 +331,7 @@ export async function syncChannelsToGrafana(): Promise { result.synced++; } catch (error) { - const errorMsg = - error instanceof Error ? error.message : String(error); + const errorMsg = error instanceof Error ? error.message : String(error); await updateAlertChannelGrafanaSync(channel.id, { grafanaSyncError: errorMsg.slice(0, 500), }); diff --git a/backend/src/utils/grafanaClient.ts b/backend/src/utils/grafanaClient.ts index d33a491..591d15a 100644 --- a/backend/src/utils/grafanaClient.ts +++ b/backend/src/utils/grafanaClient.ts @@ -116,41 +116,32 @@ export class GrafanaClient { // ============================================ async listAlertRules(): Promise { - return this.request( - "/api/v1/provisioning/alert-rules", - ); + return this.request("/api/v1/provisioning/alert-rules"); } async createAlertRule( rule: GrafanaAlertRulePayload, ): Promise { - return this.request( - "/api/v1/provisioning/alert-rules", - { - method: "POST", - body: JSON.stringify(rule), - }, - ); + return this.request("/api/v1/provisioning/alert-rules", { + method: "POST", + body: JSON.stringify(rule), + }); } async updateAlertRule( uid: string, rule: GrafanaAlertRulePayload, ): Promise { - await this.request( - `/api/v1/provisioning/alert-rules/${uid}`, - { - method: "PUT", - body: JSON.stringify(rule), - }, - ); + await this.request(`/api/v1/provisioning/alert-rules/${uid}`, { + method: "PUT", + body: JSON.stringify(rule), + }); } async deleteAlertRule(uid: string): Promise { - await this.request( - `/api/v1/provisioning/alert-rules/${uid}`, - { method: "DELETE" }, - ); + await this.request(`/api/v1/provisioning/alert-rules/${uid}`, { + method: "DELETE", + }); } // ============================================ @@ -179,20 +170,16 @@ export class GrafanaClient { uid: string, cp: GrafanaContactPointPayload, ): Promise { - await this.request( - `/api/v1/provisioning/contact-points/${uid}`, - { - method: "PUT", - body: JSON.stringify(cp), - }, - ); + await this.request(`/api/v1/provisioning/contact-points/${uid}`, { + method: "PUT", + body: JSON.stringify(cp), + }); } async deleteContactPoint(uid: string): Promise { - await this.request( - `/api/v1/provisioning/contact-points/${uid}`, - { method: "DELETE" }, - ); + await this.request(`/api/v1/provisioning/contact-points/${uid}`, { + method: "DELETE", + }); } // ============================================ diff --git a/frontend/src/components/app/app-sidebar.tsx b/frontend/src/components/app/app-sidebar.tsx index deed1e4..fb2ef95 100644 --- a/frontend/src/components/app/app-sidebar.tsx +++ b/frontend/src/components/app/app-sidebar.tsx @@ -5,6 +5,7 @@ import { BoxIcon, ChartPieIcon, ExternalLinkIcon, + FlaskConicalIcon, LayoutGridIcon, SettingsIcon, WaypointsIcon, @@ -55,6 +56,11 @@ const navItems = [ title: i18n.t('components.app.app-sidebar.Models'), href: '/models', }, + { + icon: , + title: i18n.t('components.app.app-sidebar.Playground'), + href: '/playground', + }, { icon: , title: i18n.t('components.app.app-sidebar.Settings'), diff --git a/frontend/src/components/ui/alert.tsx b/frontend/src/components/ui/alert.tsx index 1421354..56cf69a 100644 --- a/frontend/src/components/ui/alert.tsx +++ b/frontend/src/components/ui/alert.tsx @@ -1,62 +1,45 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from 'react' +import { cva, type VariantProps } from 'class-variance-authority' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' const alertVariants = cva( - "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', { variants: { variant: { - default: "bg-card text-card-foreground", + default: 'bg-card text-card-foreground', destructive: - "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90', }, }, defaultVariants: { - variant: "default", + variant: 'default', }, - } + }, ) -function Alert({ - className, - variant, - ...props -}: React.ComponentProps<"div"> & VariantProps) { - return ( -
- ) +function Alert({ className, variant, ...props }: React.ComponentProps<'div'> & VariantProps) { + return
} -function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { +function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) { return (
) } -function AlertDescription({ - className, - ...props -}: React.ComponentProps<"div">) { +function AlertDescription({ className, ...props }: React.ComponentProps<'div'>) { return (
diff --git a/frontend/src/components/ui/scroll-area.tsx b/frontend/src/components/ui/scroll-area.tsx new file mode 100644 index 0000000..2038bd6 --- /dev/null +++ b/frontend/src/components/ui/scroll-area.tsx @@ -0,0 +1,46 @@ +import * as React from 'react' +import { ScrollArea as ScrollAreaPrimitive } from 'radix-ui' + +import { cn } from '@/lib/utils' + +function ScrollArea({ className, children, ...props }: React.ComponentProps) { + return ( + + + {children} + + + + + ) +} + +function ScrollBar({ + className, + orientation = 'vertical', + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +export { ScrollArea, ScrollBar } diff --git a/frontend/src/components/ui/slider.tsx b/frontend/src/components/ui/slider.tsx new file mode 100644 index 0000000..d61a285 --- /dev/null +++ b/frontend/src/components/ui/slider.tsx @@ -0,0 +1,54 @@ +import * as React from 'react' +import { Slider as SliderPrimitive } from 'radix-ui' + +import { cn } from '@/lib/utils' + +function Slider({ + className, + defaultValue, + value, + min = 0, + max = 100, + ...props +}: React.ComponentProps) { + const _values = React.useMemo( + () => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max]), + [value, defaultValue, min, max], + ) + + return ( + + + + + {Array.from({ length: _values.length }, (_, index) => ( + + ))} + + ) +} + +export { Slider } diff --git a/frontend/src/components/ui/switch.tsx b/frontend/src/components/ui/switch.tsx index b81c340..d57fe3b 100644 --- a/frontend/src/components/ui/switch.tsx +++ b/frontend/src/components/ui/switch.tsx @@ -1,29 +1,29 @@ -import * as React from "react" -import { Switch as SwitchPrimitive } from "radix-ui" +import * as React from 'react' +import { Switch as SwitchPrimitive } from 'radix-ui' -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils' function Switch({ className, - size = "default", + size = 'default', ...props }: React.ComponentProps & { - size?: "sm" | "default" + size?: 'sm' | 'default' }) { return ( diff --git a/frontend/src/components/ui/textarea.tsx b/frontend/src/components/ui/textarea.tsx new file mode 100644 index 0000000..3809775 --- /dev/null +++ b/frontend/src/components/ui/textarea.tsx @@ -0,0 +1,18 @@ +import * as React from 'react' + +import { cn } from '@/lib/utils' + +function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) { + return ( +