diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b0b405..bb8e3ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: backend-test: name: Backend Tests diff --git a/backend/server/schemas.py b/backend/server/schemas.py index e0014a3..f48eaa0 100644 --- a/backend/server/schemas.py +++ b/backend/server/schemas.py @@ -28,7 +28,8 @@ class ChoiceQuestion(BaseQuestion): @classmethod def check_choice(cls, value: dict[str, object] | list[str]) -> dict[str, object] | list[str]: labels = list(value) - if any(not label.strip() for label in labels) or len(set(labels)) != len(labels): + normalized = [label.strip() for label in labels] + if any(not label for label in normalized) or len(set(normalized)) != len(normalized): raise ValueError("choice labels must be nonempty and unique") return value @@ -40,7 +41,8 @@ class ScoreQuestion(BaseQuestion): @field_validator("criteria") @classmethod def check_score(cls, value: list[str]) -> list[str]: - if any(not label.strip() for label in value) or len(set(value)) != len(value): + normalized = [label.strip() for label in value] + if any(not label for label in normalized) or len(set(normalized)) != len(normalized): raise ValueError("score criteria labels must be nonempty and unique") return value diff --git a/backend/tests/test_flow.py b/backend/tests/test_flow.py index 5148857..4692abd 100644 --- a/backend/tests/test_flow.py +++ b/backend/tests/test_flow.py @@ -229,6 +229,9 @@ def test_question_criteria_validation(): with pytest.raises(ValidationError, match="score criteria labels must be nonempty and unique"): ScoreQuestion(instructions="Rate", type="score", criteria=["low", " "]) + with pytest.raises(ValidationError, match="score criteria labels must be nonempty and unique"): + ScoreQuestion(instructions="Rate", type="score", criteria=["low", " low "]) + ChoiceQuestion(instructions="Choose", type="choice", criteria=["option_a", "option_b"]) ChoiceQuestion(instructions="Choose", type="choice", criteria={"option_a": "desc A", "option_b": "desc B"}) @@ -237,4 +240,3 @@ def test_question_criteria_validation(): with pytest.raises(ValidationError, match="choice labels must be nonempty and unique"): ChoiceQuestion(instructions="Choose", type="choice", criteria=["opt", " "]) - diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6d3c925..bea07b2 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -3,6 +3,7 @@ import { Activity, BookOpen, ChartNoAxesColumn, CircleHelp, Copy, KeyRound, Lang import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { apiErrorMessage, useI18n, type Locale, type MessageKey, type Translate } from "./i18n" +import { Playground, sampleRequest } from "./Playground" type ApiKey = { id: number; name: string; mask: string; created_at: string; revoked_at: string | null; last_used_at: string | null } type Usage = { totals: { requests: number; input_tokens: number; output_tokens: number }; daily: { day: string; requests: number; input_tokens: number; output_tokens: number }[]; sources: { source: string; key_id: number | null; requests: number; input_tokens: number; output_tokens: number }[] } @@ -115,7 +116,7 @@ function App() {
{title}
LAYA SERVER
-
{page === "home" ? : page === "keys" ? : page === "usage" ? : page === "playground" ? : }
+
{page === "home" ? : page === "keys" ? : page === "usage" ? : page === "playground" ? api("/internal/playground", { method: "POST", body }, session.csrf_token)} /> : }
{mobileNav ?