From 12e2fbf97a1b9cbc8ef7193102e30176789b36bc Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Mon, 13 Oct 2025 19:36:59 +0100 Subject: [PATCH 01/12] Feature: mimic the quatifier integration in python sdk --- packages/sdk/src/qualifire.ts | 44 +++++++++++++++++++++++++++++++++++ packages/sdk/src/types.ts | 9 +++++++ 2 files changed, 53 insertions(+) create mode 100644 packages/sdk/src/qualifire.ts diff --git a/packages/sdk/src/qualifire.ts b/packages/sdk/src/qualifire.ts new file mode 100644 index 00000000..624ea604 --- /dev/null +++ b/packages/sdk/src/qualifire.ts @@ -0,0 +1,44 @@ +import { EvaluationResult, ReportSummaryRequest } from "./types"; + +export class QualifireClient { + private static convertWithStructuredSummary( + evaluationResults: EvaluationResult, + request: ReportSummaryRequest + ): any { + return { + evaluations: evaluationResults, + structured: request.structuredSummary, + deep_test: request.deepTest, + start_time: request.startTime, + judge_model: request.judgeModel || null, + }; + } + + public static async reportSummaryToQualifire( + evaluationResults: EvaluationResult, + request: ReportSummaryRequest + ): Promise { + console.info("Reporting summary to Qualifire"); + + const apiEvaluationResult = this.convertWithStructuredSummary( + evaluationResults, + request + ); + + const response = await fetch(`${request.qualifireUrl}/api/rogue/v1/report`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-qualifire-key": request.qualifireApiKey, + }, + body: JSON.stringify(apiEvaluationResult), + }); + + if (!response.ok) { + const errText = await response.text(); + throw new Error( + `Qualifire report failed: ${response.status} ${response.statusText} - ${errText}` + ); + } + } +} diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index ccc1beaa..648382d6 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -118,6 +118,15 @@ export interface RogueClientConfig { retries?: number; } +export interface ReportSummaryRequest { + qualifireUrl: string; + qualifireApiKey: string; + structuredSummary: boolean; + deepTest: boolean; + startTime: string; + judgeModel?: string; +} + // Event Types for WebSocket export type WebSocketEventType = | "job_update" From 1cc20abf53f91e9b2b5a35d8fcbcd3cd99e2489d Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Tue, 14 Oct 2025 12:25:23 +0100 Subject: [PATCH 02/12] Fix: resolved errors and warnings as suggested --- packages/sdk/src/quatifier.ts | 80 +++++++++++++++++++++++++++++++++++ packages/sdk/src/types.ts | 10 ++++- 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 packages/sdk/src/quatifier.ts diff --git a/packages/sdk/src/quatifier.ts b/packages/sdk/src/quatifier.ts new file mode 100644 index 00000000..7a33f100 --- /dev/null +++ b/packages/sdk/src/quatifier.ts @@ -0,0 +1,80 @@ +import { EvaluationResult, ReportSummaryRequest, StructuredSummary } from "./types"; + +interface QualifireReportPayload { + job_id: string; + evaluations: EvaluationResult; + structured: StructuredSummary; + deep_test: boolean; + start_time: string; + judge_model: string | null; +} + +export interface QualifireClientOptions { + logger?: (message: string) => void; +} + +export class QualifireClient { + private static convertWithStructuredSummary( + evaluationResults: EvaluationResult, + request: ReportSummaryRequest + ): QualifireReportPayload { + return { + job_id: request.job_id, + evaluations: evaluationResults, + structured: request.structuredSummary || null, + deep_test: request.deepTest, + start_time: request.startTime, + judge_model: request.judgeModel || null, + }; + } + + /** + * Reports evaluation summary to quatifier. + * + * @param evaluationResults - The evaluation results to report + * @param request - Configuration including Qualifire URL, API key, and metadata + * @throws {Error} If the API request fails or returns a non-2xx status + * @returns A promise that resolves when the report is successfully submitted + */ + public static async reportSummaryToQualifire( + evaluationResults: EvaluationResult, + request: ReportSummaryRequest, + options: QualifireClientOptions + ): Promise { + options?.logger?.("Reporting summary to Qualifire"); + + const apiEvaluationResult = this.convertWithStructuredSummary( + evaluationResults, + request + ); + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 3000); + + try { + const response = await fetch(`${request.qualifireUrl}/api/evaluation/evaluate`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Qualifire-API-Key": request.qualifireApiKey, + }, + body: JSON.stringify(apiEvaluationResult), + signal: controller.signal + }); + + if (!response.ok) { + const errText = await response.text(); + throw new Error( + `Qualifire report failed: ${response.status} ${response.statusText} - ${errText}` + ); + } + clearTimeout(timeoutId) + } catch (error) { + clearTimeout(timeoutId); + if (error instanceof Error && error.name === 'AbortError') { + throw new Error('Qualifire report timed out after 30 seconds'); + } + throw error; + } + } +} diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 648382d6..e833498c 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -118,10 +118,18 @@ export interface RogueClientConfig { retries?: number; } +export interface StructuredSummary { + overall_summary: string, + key_findings: string[], + recommendations: string[], + detailed_breakdown: object[] +} + export interface ReportSummaryRequest { + job_id: string, qualifireUrl: string; qualifireApiKey: string; - structuredSummary: boolean; + structuredSummary: StructuredSummary; deepTest: boolean; startTime: string; judgeModel?: string; From 800f931e475eb690b09204e48120fa69644be89f Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Tue, 14 Oct 2025 19:26:40 +0100 Subject: [PATCH 03/12] Fix: minor errors and warnings --- packages/sdk/src/qualifire.ts | 74 ++++++++++++++++++++++++++--------- packages/sdk/src/types.ts | 2 +- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/packages/sdk/src/qualifire.ts b/packages/sdk/src/qualifire.ts index 624ea604..e25e2540 100644 --- a/packages/sdk/src/qualifire.ts +++ b/packages/sdk/src/qualifire.ts @@ -1,44 +1,80 @@ -import { EvaluationResult, ReportSummaryRequest } from "./types"; +import { EvaluationResult, ReportSummaryRequest, StructuredSummary } from "./types"; + +interface QualifireReportPayload { + job_id: string; + evaluations: EvaluationResult; + structured: StructuredSummary | null; + deep_test: boolean; + start_time: string; + judge_model: string | null; +} + +export interface QualifireClientOptions { + logger?: (message: string) => void; +} export class QualifireClient { private static convertWithStructuredSummary( evaluationResults: EvaluationResult, request: ReportSummaryRequest - ): any { + ): QualifireReportPayload { return { + job_id: request.job_id, evaluations: evaluationResults, - structured: request.structuredSummary, + structured: request.structuredSummary || null, deep_test: request.deepTest, start_time: request.startTime, judge_model: request.judgeModel || null, }; } + /** + * Reports evaluation summary to Qualifire. + * + * @param evaluationResults - The evaluation results to report + * @param request - Configuration including Qualifire URL, API key, and metadata + * @throws {Error} If the API request fails or returns a non-2xx status + * @returns A promise that resolves when the report is successfully submitted + */ public static async reportSummaryToQualifire( evaluationResults: EvaluationResult, - request: ReportSummaryRequest + request: ReportSummaryRequest, + options: QualifireClientOptions ): Promise { - console.info("Reporting summary to Qualifire"); + options?.logger?.("Reporting summary to Qualifire"); const apiEvaluationResult = this.convertWithStructuredSummary( evaluationResults, request ); - const response = await fetch(`${request.qualifireUrl}/api/rogue/v1/report`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-qualifire-key": request.qualifireApiKey, - }, - body: JSON.stringify(apiEvaluationResult), - }); - - if (!response.ok) { - const errText = await response.text(); - throw new Error( - `Qualifire report failed: ${response.status} ${response.statusText} - ${errText}` - ); + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 3000); + + try { + const response = await fetch(`${request.qualifireUrl}/api/evaluation/evaluate`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Qualifire-API-Key": request.qualifireApiKey, + }, + body: JSON.stringify(apiEvaluationResult), + signal: controller.signal + }); + + if (!response.ok) { + const errText = await response.text(); + throw new Error( + `Qualifire report failed: ${response.status} ${response.statusText} - ${errText}` + ); + } + clearTimeout(timeoutId); + } catch (error) { + clearTimeout(timeoutId); + if (error instanceof Error && error.name === 'AbortError') { + throw new Error('Qualifire report timed out after 3 seconds'); + } + throw error; } } } diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index e833498c..7d21993a 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -129,7 +129,7 @@ export interface ReportSummaryRequest { job_id: string, qualifireUrl: string; qualifireApiKey: string; - structuredSummary: StructuredSummary; + structuredSummary?: StructuredSummary; deepTest: boolean; startTime: string; judgeModel?: string; From eb9b8cc5025d9453ccf7172292c4b7132d0cb951 Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Tue, 14 Oct 2025 19:38:46 +0100 Subject: [PATCH 04/12] Fix: minor errors and warnings --- packages/sdk/src/qualifire.ts | 2 +- packages/sdk/src/types.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/sdk/src/qualifire.ts b/packages/sdk/src/qualifire.ts index e25e2540..b81f705f 100644 --- a/packages/sdk/src/qualifire.ts +++ b/packages/sdk/src/qualifire.ts @@ -39,7 +39,7 @@ export class QualifireClient { public static async reportSummaryToQualifire( evaluationResults: EvaluationResult, request: ReportSummaryRequest, - options: QualifireClientOptions + options?: QualifireClientOptions ): Promise { options?.logger?.("Reporting summary to Qualifire"); diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 7d21993a..c2f6f1e1 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -127,11 +127,11 @@ export interface StructuredSummary { export interface ReportSummaryRequest { job_id: string, - qualifireUrl: string; - qualifireApiKey: string; + qualifireUrl?: string; + qualifireApiKey?: string; structuredSummary?: StructuredSummary; - deepTest: boolean; - startTime: string; + deepTest?: boolean; + startTime?: string; judgeModel?: string; } From c17f366637d4b8a730b95ac60de2162bd024a319 Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Wed, 15 Oct 2025 09:23:33 +0100 Subject: [PATCH 05/12] Fix: removed mispelled file quatifier.ts --- packages/sdk/src/quatifier.ts | 80 ----------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 packages/sdk/src/quatifier.ts diff --git a/packages/sdk/src/quatifier.ts b/packages/sdk/src/quatifier.ts deleted file mode 100644 index 7a33f100..00000000 --- a/packages/sdk/src/quatifier.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { EvaluationResult, ReportSummaryRequest, StructuredSummary } from "./types"; - -interface QualifireReportPayload { - job_id: string; - evaluations: EvaluationResult; - structured: StructuredSummary; - deep_test: boolean; - start_time: string; - judge_model: string | null; -} - -export interface QualifireClientOptions { - logger?: (message: string) => void; -} - -export class QualifireClient { - private static convertWithStructuredSummary( - evaluationResults: EvaluationResult, - request: ReportSummaryRequest - ): QualifireReportPayload { - return { - job_id: request.job_id, - evaluations: evaluationResults, - structured: request.structuredSummary || null, - deep_test: request.deepTest, - start_time: request.startTime, - judge_model: request.judgeModel || null, - }; - } - - /** - * Reports evaluation summary to quatifier. - * - * @param evaluationResults - The evaluation results to report - * @param request - Configuration including Qualifire URL, API key, and metadata - * @throws {Error} If the API request fails or returns a non-2xx status - * @returns A promise that resolves when the report is successfully submitted - */ - public static async reportSummaryToQualifire( - evaluationResults: EvaluationResult, - request: ReportSummaryRequest, - options: QualifireClientOptions - ): Promise { - options?.logger?.("Reporting summary to Qualifire"); - - const apiEvaluationResult = this.convertWithStructuredSummary( - evaluationResults, - request - ); - - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 3000); - - try { - const response = await fetch(`${request.qualifireUrl}/api/evaluation/evaluate`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-Qualifire-API-Key": request.qualifireApiKey, - }, - body: JSON.stringify(apiEvaluationResult), - signal: controller.signal - }); - - if (!response.ok) { - const errText = await response.text(); - throw new Error( - `Qualifire report failed: ${response.status} ${response.statusText} - ${errText}` - ); - } - clearTimeout(timeoutId) - } catch (error) { - clearTimeout(timeoutId); - if (error instanceof Error && error.name === 'AbortError') { - throw new Error('Qualifire report timed out after 30 seconds'); - } - throw error; - } - } -} From 37d81523a53d049a4a9d5b10651189a45a6888ed Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Wed, 15 Oct 2025 09:27:35 +0100 Subject: [PATCH 06/12] Fix: minor/potential errors --- packages/sdk/src/qualifire.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/qualifire.ts b/packages/sdk/src/qualifire.ts index b81f705f..0049eb27 100644 --- a/packages/sdk/src/qualifire.ts +++ b/packages/sdk/src/qualifire.ts @@ -49,7 +49,7 @@ export class QualifireClient { ); const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 3000); + const timeoutId = setTimeout(() => controller.abort(), 30000); try { const response = await fetch(`${request.qualifireUrl}/api/evaluation/evaluate`, { @@ -72,7 +72,7 @@ export class QualifireClient { } catch (error) { clearTimeout(timeoutId); if (error instanceof Error && error.name === 'AbortError') { - throw new Error('Qualifire report timed out after 3 seconds'); + throw new Error('Qualifire report timed out after 30 seconds'); } throw error; } From d9d32737a075fc535605873fe13ebaaddf221d2e Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Sun, 19 Oct 2025 12:17:33 +0100 Subject: [PATCH 07/12] Fix: some potential issues as the cursor bot suggested --- packages/sdk/src/qualifire.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/sdk/src/qualifire.ts b/packages/sdk/src/qualifire.ts index 0049eb27..1983d5ee 100644 --- a/packages/sdk/src/qualifire.ts +++ b/packages/sdk/src/qualifire.ts @@ -22,8 +22,8 @@ export class QualifireClient { job_id: request.job_id, evaluations: evaluationResults, structured: request.structuredSummary || null, - deep_test: request.deepTest, - start_time: request.startTime, + deep_test: request.deepTest ?? false, + start_time: request.startTime ?? new Date().toISOString(), judge_model: request.judgeModel || null, }; } @@ -43,6 +43,18 @@ export class QualifireClient { ): Promise { options?.logger?.("Reporting summary to Qualifire"); + const apiKey = request.qualifireApiKey; + const baseUrl = request.qualifireUrl ?? "https://api.qualifire.com"; + const endpoint = `${baseUrl}/api/evaluation/evaluate`; + + if (!apiKey) { + throw new Error("qualifireApiKey is required but was undefined"); + } + + if (!baseUrl || baseUrl === "undefined") { + throw new Error("Invalid qualifireUrl provided"); + } + const apiEvaluationResult = this.convertWithStructuredSummary( evaluationResults, request @@ -52,11 +64,11 @@ export class QualifireClient { const timeoutId = setTimeout(() => controller.abort(), 30000); try { - const response = await fetch(`${request.qualifireUrl}/api/evaluation/evaluate`, { + const response = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json", - "X-Qualifire-API-Key": request.qualifireApiKey, + "X-Qualifire-API-Key": apiKey, }, body: JSON.stringify(apiEvaluationResult), signal: controller.signal From 673b0c378ed5e888e2f48250e87864a658f6c16e Mon Sep 17 00:00:00 2001 From: yuval-qf Date: Thu, 23 Oct 2025 16:47:12 +0300 Subject: [PATCH 08/12] Skip rogue sanity on fork PRs (#116) --- .github/workflows/rogue.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/rogue.yml b/.github/workflows/rogue.yml index a967443c..edd50d72 100644 --- a/.github/workflows/rogue.yml +++ b/.github/workflows/rogue.yml @@ -1,7 +1,7 @@ name: Rogue on: - pull_request_target: + pull_request: push: branches: - main @@ -11,17 +11,11 @@ jobs: rogue_sanity: runs-on: ubuntu-latest timeout-minutes: 15 - environment: rogue-sanity-ci-secrets + # Only run for PRs from within the same repository, not from forks + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository steps: - name: Checkout Repository uses: actions/checkout@v4 - with: - # 1. Checkout the actual PR commit, not just the base branch - ref: ${{ github.event.pull_request.head.sha }} - # 2. DO NOT expose the GITHUB_TOKEN write-permissions to the untrusted code - # This is essential to prevent untrusted code from exfiltrating secrets - # by manipulating the repository itself. - persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@v5 From db079ce46819a10201e1e665a14babae621cbb94 Mon Sep 17 00:00:00 2001 From: yuval-qf Date: Fri, 24 Oct 2025 00:19:37 +0300 Subject: [PATCH 09/12] FIRE-812 | Rogue-TUI | MCP Support (#115) * Add protocol and transport settings to tui * Fix mcp example command * Fix log_evaluation kwargs --- .gitignore | 3 + .vscode/launch.json | 8 ++ .../tshirt_store_langgraph_mcp/__main__.py | 2 +- .../tui/internal/tui/common_controller.go | 41 ++++--- packages/tui/internal/tui/eval_form.go | 94 +++++++++++++-- .../tui/internal/tui/eval_form_controller.go | 54 +++++---- packages/tui/internal/tui/eval_ui.go | 114 ++++++++++++++++-- packages/tui/internal/tui/evaluation.go | 32 +++-- .../tui/internal/tui/keyboard_controller.go | 23 ++-- rogue/evaluator_agent/base_evaluator_agent.py | 1 + 10 files changed, 290 insertions(+), 82 deletions(-) diff --git a/.gitignore b/.gitignore index 91550c18..4bbf4544 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,6 @@ dist/ .adk/ **/.rogue bin/ + +# Go TUI +packages/tui/cmd/rogue/__debug* diff --git a/.vscode/launch.json b/.vscode/launch.json index 3765647c..96d9bb38 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -63,6 +63,14 @@ ], "envFile": "${workspaceFolder}/.env" }, + { + "name": "Rogue TUI", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/packages/tui/cmd/rogue/main.go", + "console": "integratedTerminal" + }, { "name": "Rogue AIO", "type": "debugpy", diff --git a/examples/mcp/tshirt_store_langgraph_mcp/__main__.py b/examples/mcp/tshirt_store_langgraph_mcp/__main__.py index f0cb66a1..fcb7a894 100644 --- a/examples/mcp/tshirt_store_langgraph_mcp/__main__.py +++ b/examples/mcp/tshirt_store_langgraph_mcp/__main__.py @@ -15,7 +15,7 @@ "--transport", "transport", default="streamable-http", - choices=["streamable-http", "sse"], + type=click.Choice(["streamable-http", "sse"]), help="Transport to use for the mcp server", ) def main(host: str, port: int, transport: Literal["streamable-http", "sse"]) -> None: diff --git a/packages/tui/internal/tui/common_controller.go b/packages/tui/internal/tui/common_controller.go index 1d947a3c..4ad264be 100644 --- a/packages/tui/internal/tui/common_controller.go +++ b/packages/tui/internal/tui/common_controller.go @@ -45,19 +45,17 @@ func (m Model) handlePasteMsg(msg tea.PasteMsg) (Model, tea.Cmd) { } // Only paste into text fields (Agent URL and Judge Model) - if m.evalState.currentField <= 1 { - switch m.evalState.currentField { - case 0: // Agent URL - // Insert at cursor position - runes := []rune(m.evalState.AgentURL) - m.evalState.AgentURL = string(runes[:m.evalState.cursorPos]) + cleanText + string(runes[m.evalState.cursorPos:]) - m.evalState.cursorPos += len([]rune(cleanText)) - case 1: // Judge Model - // Insert at cursor position - runes := []rune(m.evalState.JudgeModel) - m.evalState.JudgeModel = string(runes[:m.evalState.cursorPos]) + cleanText + string(runes[m.evalState.cursorPos:]) - m.evalState.cursorPos += len([]rune(cleanText)) - } + switch m.evalState.currentField { + case 0: // Agent URL + // Insert at cursor position + runes := []rune(m.evalState.AgentURL) + m.evalState.AgentURL = string(runes[:m.evalState.cursorPos]) + cleanText + string(runes[m.evalState.cursorPos:]) + m.evalState.cursorPos += len([]rune(cleanText)) + case 3: // Judge Model + // Insert at cursor position + runes := []rune(m.evalState.JudgeModel) + m.evalState.JudgeModel = string(runes[:m.evalState.cursorPos]) + cleanText + string(runes[m.evalState.cursorPos:]) + m.evalState.cursorPos += len([]rune(cleanText)) } return m, nil } @@ -202,14 +200,17 @@ func (m Model) handleCommandSelectedMsg(msg components.CommandSelectedMsg) (Mode // Use the configured model in provider/model format judgeModel = m.config.SelectedProvider + "/" + m.config.SelectedModel } + // TODO read agent url and protocol .rogue/user_config.json m.evalState = &EvaluationViewState{ - ServerURL: m.config.ServerURL, - AgentURL: "http://localhost:10001", - JudgeModel: judgeModel, - ParallelRuns: 1, - DeepTest: false, - Scenarios: loadScenariosFromWorkdir(), - cursorPos: len([]rune("http://localhost:10001")), // Set cursor to end of Agent URL + ServerURL: m.config.ServerURL, + AgentURL: "http://localhost:10001", + AgentProtocol: ProtocolA2A, + AgentTransport: TransportHTTP, + JudgeModel: judgeModel, + ParallelRuns: 1, + DeepTest: false, + Scenarios: loadScenariosFromWorkdir(), + cursorPos: len([]rune("http://localhost:10001")), // Set cursor to end of Agent URL } case "configure_models": // Open LLM configuration dialog diff --git a/packages/tui/internal/tui/eval_form.go b/packages/tui/internal/tui/eval_form.go index d52bdb11..760f96d2 100644 --- a/packages/tui/internal/tui/eval_form.go +++ b/packages/tui/internal/tui/eval_form.go @@ -37,8 +37,8 @@ func (m Model) renderNewEvaluation() string { title := titleStyle.Render("๐Ÿงช New Evaluation") - // Helper function to render a field with inline label and value - renderField := func(fieldIndex int, label, value string) string { + // Helper function to render a text field with inline label and value + renderTextField := func(fieldIndex int, label, value string) string { active := m.evalState.currentField == fieldIndex labelStyle := lipgloss.NewStyle(). @@ -83,8 +83,86 @@ func (m Model) renderNewEvaluation() string { return fieldContainer.Render(fieldContent) } + // Helper function to render a dropdown field with indicators + renderDropdownField := func(fieldIndex int, label, value string) string { + active := m.evalState.currentField == fieldIndex + + labelStyle := lipgloss.NewStyle(). + Foreground(t.TextMuted()). + Background(t.Background()). + Width(20). + Align(lipgloss.Right) + + valueStyle := lipgloss.NewStyle(). + Foreground(t.Text()). + Background(t.Background()). + Padding(0, 1) + + if active { + labelStyle = labelStyle.Foreground(t.Primary()).Bold(true) + valueStyle = valueStyle. + Foreground(t.Primary()). + Background(t.Background()). + Bold(true) + // Add dropdown indicators + value = "โ—€ " + value + " โ–ถ" + } + + // Create a full-width container for the field + fieldContainer := lipgloss.NewStyle(). + Width(m.width-4). + Background(t.Background()). + Padding(0, 2) + + fieldContent := lipgloss.JoinHorizontal(lipgloss.Left, + labelStyle.Render(label), + valueStyle.Render(value), + ) + + return fieldContainer.Render(fieldContent) + } + + // Helper function to render a toggle field + renderToggleField := func(fieldIndex int, label, value string) string { + active := m.evalState.currentField == fieldIndex + + labelStyle := lipgloss.NewStyle(). + Foreground(t.TextMuted()). + Background(t.Background()). + Width(20). + Align(lipgloss.Right) + + valueStyle := lipgloss.NewStyle(). + Foreground(t.Text()). + Background(t.Background()). + Padding(0, 1) + + if active { + labelStyle = labelStyle.Foreground(t.Primary()).Bold(true) + valueStyle = valueStyle. + Foreground(t.Primary()). + Background(t.Background()). + Bold(true) + } + + // Create a full-width container for the field + fieldContainer := lipgloss.NewStyle(). + Width(m.width-4). + Background(t.Background()). + Padding(0, 2) + + fieldContent := lipgloss.JoinHorizontal(lipgloss.Left, + labelStyle.Render(label), + valueStyle.Render(value), + ) + + return fieldContainer.Render(fieldContent) + } + // Prepare field values agent := m.evalState.AgentURL + protocol := string(m.evalState.AgentProtocol) + transport := string(m.evalState.AgentTransport) judge := m.evalState.JudgeModel deep := "โŒ" if m.evalState.DeepTest { @@ -93,7 +171,7 @@ func (m Model) renderNewEvaluation() string { // Helper function to render the start button renderStartButton := func() string { - active := m.evalState.currentField == 3 + active := m.evalState.currentField == 5 var buttonText string if m.evalSpinner.IsActive() { @@ -151,9 +229,11 @@ func (m Model) renderNewEvaluation() string { // Build the content sections formSection := lipgloss.JoinVertical(lipgloss.Left, - renderField(0, "Agent URL:", agent), - renderField(1, "Judge LLM:", judge), - renderField(2, "Deep Test:", deep), + renderTextField(0, "Agent URL:", agent), + renderDropdownField(1, "Protocol:", protocol), + renderDropdownField(2, "Transport:", transport), + renderTextField(3, "Judge LLM:", judge), + renderToggleField(4, "Deep Test:", deep), ) var infoLines []string @@ -168,7 +248,7 @@ func (m Model) renderNewEvaluation() string { buttonSection := renderStartButton() - helpText := helpStyle.Render("t Test Server โ†‘/โ†“ switch fields โ†/โ†’ move cursor Space toggle Enter activate Esc Back") + helpText := helpStyle.Render("t Test Server โ†‘/โ†“ switch fields โ†/โ†’ move cursor/cycle dropdown Space toggle Enter activate Esc Back") // Calculate content area height (excluding title and help) contentHeight := m.height - 6 // title(3) + help(1) + margins(2) diff --git a/packages/tui/internal/tui/eval_form_controller.go b/packages/tui/internal/tui/eval_form_controller.go index 558e274b..e5684055 100644 --- a/packages/tui/internal/tui/eval_form_controller.go +++ b/packages/tui/internal/tui/eval_form_controller.go @@ -26,7 +26,7 @@ func (m Model) handleEvalFormInput(msg tea.KeyMsg) (Model, tea.Cmd) { switch m.evalState.currentField { case 0: m.evalState.cursorPos = len([]rune(m.evalState.AgentURL)) - case 1: + case 3: m.evalState.cursorPos = len([]rune(m.evalState.JudgeModel)) default: m.evalState.cursorPos = 0 @@ -35,13 +35,13 @@ func (m Model) handleEvalFormInput(msg tea.KeyMsg) (Model, tea.Cmd) { return m, nil case "down": - if m.evalState.currentField < 3 { // Now includes start button (0-3) + if m.evalState.currentField < 5 { // 0-5 fields (AgentURL, Protocol, Transport, JudgeModel, DeepTest, StartButton) m.evalState.currentField++ // Set cursor to end of field content when switching fields switch m.evalState.currentField { case 0: m.evalState.cursorPos = len([]rune(m.evalState.AgentURL)) - case 1: + case 3: m.evalState.cursorPos = len([]rune(m.evalState.JudgeModel)) default: m.evalState.cursorPos = 0 @@ -50,23 +50,31 @@ func (m Model) handleEvalFormInput(msg tea.KeyMsg) (Model, tea.Cmd) { return m, nil case "left": - if m.evalState.currentField <= 1 && m.evalState.cursorPos > 0 { // Text fields 0-2 - m.evalState.cursorPos-- + switch m.evalState.currentField { + case 0, 3: // Text fields: AgentURL, JudgeModel + if m.evalState.cursorPos > 0 { + m.evalState.cursorPos-- + } + case 1: // Protocol dropdown + m.evalState.cycleProtocol(true) // cycle backwards + case 2: // Transport dropdown + m.evalState.cycleTransport(true) // cycle backwards } return m, nil case "right": - if m.evalState.currentField <= 1 { // Text fields 0-1 - // Get current field length to limit cursor - var fieldLen int - switch m.evalState.currentField { - case 0: - fieldLen = len(m.evalState.AgentURL) - case 1: - fieldLen = len(m.evalState.JudgeModel) - case 2: - fieldLen = len(fmt.Sprintf("%d", m.evalState.ParallelRuns)) + switch m.evalState.currentField { + case 0: // AgentURL text field + fieldLen := len(m.evalState.AgentURL) + if m.evalState.cursorPos < fieldLen { + m.evalState.cursorPos++ } + case 1: // Protocol dropdown + m.evalState.cycleProtocol(false) // cycle forwards + case 2: // Transport dropdown + m.evalState.cycleTransport(false) // cycle forwards + case 3: // JudgeModel text field + fieldLen := len(m.evalState.JudgeModel) if m.evalState.cursorPos < fieldLen { m.evalState.cursorPos++ } @@ -74,14 +82,14 @@ func (m Model) handleEvalFormInput(msg tea.KeyMsg) (Model, tea.Cmd) { return m, nil case "space": - if m.evalState.currentField == 2 { // DeepTest field is now index 2 + if m.evalState.currentField == 4 { // DeepTest field is now index 2 m.evalState.DeepTest = !m.evalState.DeepTest return m, nil } case "tab": // Open LLM config dialog when on Judge Model field - if m.evalState.currentField == 1 { // JudgeModel field + if m.evalState.currentField == 3 { // JudgeModel field llmDialog := components.NewLLMConfigDialog(m.config.APIKeys, m.config.SelectedProvider, m.config.SelectedModel) m.llmDialog = &llmDialog return m, nil @@ -89,7 +97,7 @@ func (m Model) handleEvalFormInput(msg tea.KeyMsg) (Model, tea.Cmd) { case "backspace": // Handle backspace for text fields - if m.evalState.currentField <= 1 && m.evalState.cursorPos > 0 { + if m.evalState.currentField > 0 { switch m.evalState.currentField { case 0: // AgentURL runes := []rune(m.evalState.AgentURL) @@ -97,13 +105,13 @@ func (m Model) handleEvalFormInput(msg tea.KeyMsg) (Model, tea.Cmd) { m.evalState.AgentURL = string(runes[:m.evalState.cursorPos-1]) + string(runes[m.evalState.cursorPos:]) m.evalState.cursorPos-- } - case 1: // JudgeModel + case 3: // JudgeModel runes := []rune(m.evalState.JudgeModel) if m.evalState.cursorPos <= len(runes) { m.evalState.JudgeModel = string(runes[:m.evalState.cursorPos-1]) + string(runes[m.evalState.cursorPos:]) m.evalState.cursorPos-- } - case 2: // ParallelRuns (special handling for numbers) + case 6: // ParallelRuns (special handling for numbers) if m.evalState.ParallelRuns >= 10 { m.evalState.ParallelRuns /= 10 m.evalState.cursorPos-- @@ -118,17 +126,17 @@ func (m Model) handleEvalFormInput(msg tea.KeyMsg) (Model, tea.Cmd) { default: // insert character into text fields s := msg.String() - if len(s) == 1 && m.evalState.currentField <= 2 { // Text fields 0-2 + if len(s) == 1 { switch m.evalState.currentField { case 0: // AgentURL runes := []rune(m.evalState.AgentURL) m.evalState.AgentURL = string(runes[:m.evalState.cursorPos]) + s + string(runes[m.evalState.cursorPos:]) m.evalState.cursorPos++ - case 1: // JudgeModel + case 3: // JudgeModel runes := []rune(m.evalState.JudgeModel) m.evalState.JudgeModel = string(runes[:m.evalState.cursorPos]) + s + string(runes[m.evalState.cursorPos:]) m.evalState.cursorPos++ - case 2: // ParallelRuns (numeric only) + case 6: // ParallelRuns (numeric only) if s[0] >= '0' && s[0] <= '9' { numStr := fmt.Sprintf("%d", m.evalState.ParallelRuns) runes := []rune(numStr) diff --git a/packages/tui/internal/tui/eval_ui.go b/packages/tui/internal/tui/eval_ui.go index 76a553be..bbadd423 100644 --- a/packages/tui/internal/tui/eval_ui.go +++ b/packages/tui/internal/tui/eval_ui.go @@ -7,14 +7,34 @@ import ( "path/filepath" ) +type Protocol string + +const ( + ProtocolA2A Protocol = "a2a" + ProtocolMCP Protocol = "mcp" +) + +type Transport string + +const ( + // mcp transports + TransportSSE Transport = "sse" + TransportStreamableHTTP Transport = "streamable_http" + + // a2a transports + TransportHTTP Transport = "http" +) + // Minimal state for eval screens type EvaluationViewState struct { - ServerURL string // Used from config, not editable in form - AgentURL string - JudgeModel string - ParallelRuns int - DeepTest bool - Scenarios []EvalScenario + ServerURL string // Used from config, not editable in form + AgentURL string + AgentProtocol Protocol + AgentTransport Transport + JudgeModel string + ParallelRuns int + DeepTest bool + Scenarios []EvalScenario // Runtime Running bool @@ -31,7 +51,7 @@ type EvaluationViewState struct { StructuredSummary StructuredSummary // Editing state for New Evaluation - currentField int // 0: AgentURL, 1: JudgeModel, 2: DeepTest, 3: StartButton + currentField int // 0: AgentURL, 1: Protocol, 2: Transport, 3: JudgeModel, 4: DeepTest, 5: StartButton cursorPos int // rune index in current text field } @@ -74,7 +94,7 @@ func loadScenariosFromWorkdir() []EvalScenario { // startEval kicks off evaluation and consumes events into state func (m *Model) startEval(ctx context.Context, st *EvaluationViewState) { - ch, cancel, err := m.StartEvaluation(ctx, st.ServerURL, st.AgentURL, st.Scenarios, st.JudgeModel, st.ParallelRuns, st.DeepTest) + ch, cancel, err := m.StartEvaluation(ctx, st.ServerURL, st.AgentURL, st.AgentProtocol, st.AgentTransport, st.Scenarios, st.JudgeModel, st.ParallelRuns, st.DeepTest) if err != nil { st.Running = false st.Status = "error" @@ -123,3 +143,81 @@ func (m *Model) triggerSummaryGeneration() { // Start spinner for automatic summary generation m.summarySpinner.SetActive(true) } + +// getAllProtocols returns all available protocol options +func getAllProtocols() []Protocol { + return []Protocol{ProtocolA2A, ProtocolMCP} +} + +// getTransportsForProtocol returns valid transport options for a given protocol +func getTransportsForProtocol(protocol Protocol) []Transport { + switch protocol { + case ProtocolMCP: + return []Transport{TransportStreamableHTTP, TransportSSE} + case ProtocolA2A: + return []Transport{TransportHTTP} + default: + return []Transport{} + } +} + +// cycleProtocol cycles to the next protocol option +func (st *EvaluationViewState) cycleProtocol(reverse bool) { + protocols := getAllProtocols() + currentIdx := -1 + for i, p := range protocols { + if p == st.AgentProtocol { + currentIdx = i + break + } + } + + if reverse { + currentIdx-- + if currentIdx < 0 { + currentIdx = len(protocols) - 1 + } + } else { + currentIdx++ + if currentIdx >= len(protocols) { + currentIdx = 0 + } + } + + st.AgentProtocol = protocols[currentIdx] + // Reset transport to first valid option for new protocol + validTransports := getTransportsForProtocol(st.AgentProtocol) + if len(validTransports) > 0 { + st.AgentTransport = validTransports[0] + } +} + +// cycleTransport cycles to the next transport option for the current protocol +func (st *EvaluationViewState) cycleTransport(reverse bool) { + transports := getTransportsForProtocol(st.AgentProtocol) + if len(transports) == 0 { + return + } + + currentIdx := -1 + for i, t := range transports { + if t == st.AgentTransport { + currentIdx = i + break + } + } + + if reverse { + currentIdx-- + if currentIdx < 0 { + currentIdx = len(transports) - 1 + } + } else { + currentIdx++ + if currentIdx >= len(transports) { + currentIdx = 0 + } + } + + st.AgentTransport = transports[currentIdx] +} diff --git a/packages/tui/internal/tui/evaluation.go b/packages/tui/internal/tui/evaluation.go index 6b021191..952b62eb 100644 --- a/packages/tui/internal/tui/evaluation.go +++ b/packages/tui/internal/tui/evaluation.go @@ -32,13 +32,15 @@ const ( ) type AgentConfig struct { - EvaluatedAgentURL string `json:"evaluated_agent_url"` - EvaluatedAgentAuthType AuthType `json:"evaluated_agent_auth_type"` - EvaluatedAgentCredentials string `json:"evaluated_agent_credentials,omitempty"` - JudgeLLMModel string `json:"judge_llm"` - InterviewMode bool `json:"interview_mode"` - DeepTestMode bool `json:"deep_test_mode"` - ParallelRuns int `json:"parallel_runs"` + EvaluatedAgentURL string `json:"evaluated_agent_url"` + EvaluatedAgentProtocol Protocol `json:"protocol"` + EvaluatedAgentTransport Transport `json:"transport"` + EvaluatedAgentAuthType AuthType `json:"evaluated_agent_auth_type"` + EvaluatedAgentCredentials string `json:"evaluated_agent_credentials,omitempty"` + JudgeLLMModel string `json:"judge_llm"` + InterviewMode bool `json:"interview_mode"` + DeepTestMode bool `json:"deep_test_mode"` + ParallelRuns int `json:"parallel_runs"` } type EvalScenario struct { @@ -421,6 +423,8 @@ func (m *Model) StartEvaluation( ctx context.Context, serverURL string, agentURL string, + agentProtocol Protocol, + agentTransport Transport, scenarios []EvalScenario, judgeModel string, parallelRuns int, @@ -439,12 +443,14 @@ func (m *Model) StartEvaluation( // Build evaluation request request := EvaluationRequest{ AgentConfig: AgentConfig{ - EvaluatedAgentURL: agentURL, - EvaluatedAgentAuthType: AuthTypeNoAuth, - JudgeLLMModel: judgeModel, - InterviewMode: true, - DeepTestMode: deepTest, - ParallelRuns: parallelRuns, + EvaluatedAgentURL: agentURL, + EvaluatedAgentProtocol: agentProtocol, + EvaluatedAgentTransport: agentTransport, + EvaluatedAgentAuthType: AuthTypeNoAuth, + JudgeLLMModel: judgeModel, + InterviewMode: true, + DeepTestMode: deepTest, + ParallelRuns: parallelRuns, }, MaxRetries: 3, TimeoutSeconds: 600, diff --git a/packages/tui/internal/tui/keyboard_controller.go b/packages/tui/internal/tui/keyboard_controller.go index 41d5528b..c38b93a9 100644 --- a/packages/tui/internal/tui/keyboard_controller.go +++ b/packages/tui/internal/tui/keyboard_controller.go @@ -77,14 +77,17 @@ func (m Model) handleGlobalCtrlN() (Model, tea.Cmd) { // Use the configured model in provider/model format judgeModel = m.config.SelectedProvider + "/" + m.config.SelectedModel } + // TODO read agent url and protocol .rogue/user_config.json m.evalState = &EvaluationViewState{ - ServerURL: m.config.ServerURL, - AgentURL: "http://localhost:10001", - JudgeModel: judgeModel, - ParallelRuns: 1, - DeepTest: false, - Scenarios: loadScenariosFromWorkdir(), - cursorPos: len([]rune("http://localhost:10001")), // Set cursor to end of Agent URL + ServerURL: m.config.ServerURL, + AgentURL: "http://localhost:10001", + AgentProtocol: ProtocolA2A, + AgentTransport: TransportHTTP, + JudgeModel: judgeModel, + ParallelRuns: 1, + DeepTest: false, + Scenarios: loadScenariosFromWorkdir(), + cursorPos: len([]rune("http://localhost:10001")), // Set cursor to end of Agent URL } m.currentScreen = NewEvaluationScreen return m, nil @@ -139,7 +142,7 @@ func (m Model) handleGlobalSlash(msg tea.KeyMsg) (Model, tea.Cmd) { runes := []rune(m.evalState.AgentURL) m.evalState.AgentURL = string(runes[:m.evalState.cursorPos]) + s + string(runes[m.evalState.cursorPos:]) m.evalState.cursorPos++ - case 1: // JudgeModel + case 3: // JudgeModel runes := []rune(m.evalState.JudgeModel) m.evalState.JudgeModel = string(runes[:m.evalState.cursorPos]) + s + string(runes[m.evalState.cursorPos:]) m.evalState.cursorPos++ @@ -266,11 +269,11 @@ func (m Model) handleGlobalEnter(msg tea.KeyMsg) (Model, tea.Cmd) { } // Handle NewEvaluationScreen enter for start button and LLM config if m.currentScreen == NewEvaluationScreen && m.evalState != nil { - if m.evalState.currentField == 3 { // Start button field + if m.evalState.currentField == 5 { // Start button field m.handleNewEvalEnter() // Return command to start evaluation after showing spinner return m, tea.Batch(m.evalSpinner.Start(), startEvaluationCmd()) - } else if m.evalState.currentField == 1 { // Judge LLM field + } else if m.evalState.currentField == 3 { // Judge LLM field // Open LLM config dialog when Enter is pressed on Judge LLM field llmDialog := components.NewLLMConfigDialog(m.config.APIKeys, m.config.SelectedProvider, m.config.SelectedModel) m.llmDialog = &llmDialog diff --git a/rogue/evaluator_agent/base_evaluator_agent.py b/rogue/evaluator_agent/base_evaluator_agent.py index 86f1d09e..4afdc5a0 100644 --- a/rogue/evaluator_agent/base_evaluator_agent.py +++ b/rogue/evaluator_agent/base_evaluator_agent.py @@ -366,6 +366,7 @@ def _log_evaluation( evaluation_passed: bool, reason: str, scenario_type: Optional[str], + **kwargs, ) -> None: """ Logs the evaluation of the given scenario and test case. From db44d1d62b43e6f4227c6ae58844348ab1e68cd2 Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Fri, 24 Oct 2025 13:47:50 +0100 Subject: [PATCH 10/12] Fix: update path --- packages/sdk/src/qualifire.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/src/qualifire.ts b/packages/sdk/src/qualifire.ts index 1983d5ee..a57b39b5 100644 --- a/packages/sdk/src/qualifire.ts +++ b/packages/sdk/src/qualifire.ts @@ -45,7 +45,7 @@ export class QualifireClient { const apiKey = request.qualifireApiKey; const baseUrl = request.qualifireUrl ?? "https://api.qualifire.com"; - const endpoint = `${baseUrl}/api/evaluation/evaluate`; + const endpoint = `${baseUrl}/api/v1/evaluations`; if (!apiKey) { throw new Error("qualifireApiKey is required but was undefined"); From 701181b009e59806d1c0505872fe9bf4a9fde696 Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Fri, 24 Oct 2025 14:13:09 +0100 Subject: [PATCH 11/12] Fix: wrong path --- packages/sdk/src/qualifire.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/src/qualifire.ts b/packages/sdk/src/qualifire.ts index a57b39b5..9cd4f6b6 100644 --- a/packages/sdk/src/qualifire.ts +++ b/packages/sdk/src/qualifire.ts @@ -45,7 +45,7 @@ export class QualifireClient { const apiKey = request.qualifireApiKey; const baseUrl = request.qualifireUrl ?? "https://api.qualifire.com"; - const endpoint = `${baseUrl}/api/v1/evaluations`; + const endpoint = `${baseUrl}/llm/summary`; if (!apiKey) { throw new Error("qualifireApiKey is required but was undefined"); From edc73fe4806a39e6ef827478b7e35920a6d7260a Mon Sep 17 00:00:00 2001 From: ignorant05 Date: Tue, 11 Nov 2025 14:16:13 +0100 Subject: [PATCH 12/12] Rmoved duplicates --- .../internal/screens/evaluations/form_view.go | 86 ++----------------- packages/tui/internal/tui/utils.go | 5 -- 2 files changed, 5 insertions(+), 86 deletions(-) diff --git a/packages/tui/internal/screens/evaluations/form_view.go b/packages/tui/internal/screens/evaluations/form_view.go index 14d5dbda..4062163a 100644 --- a/packages/tui/internal/screens/evaluations/form_view.go +++ b/packages/tui/internal/screens/evaluations/form_view.go @@ -159,87 +159,11 @@ func RenderForm(state *FormState) string { return fieldContainer.Render(fieldContent) } - // Helper function to render a dropdown field with indicators - renderDropdownField := func(fieldIndex int, label, value string) string { - active := m.evalState.currentField == fieldIndex - - labelStyle := lipgloss.NewStyle(). - Foreground(t.TextMuted()). - Background(t.Background()). - Width(20). - Align(lipgloss.Right) - - valueStyle := lipgloss.NewStyle(). - Foreground(t.Text()). - Background(t.Background()). - Padding(0, 1) - - if active { - labelStyle = labelStyle.Foreground(t.Primary()).Bold(true) - valueStyle = valueStyle. - Foreground(t.Primary()). - Background(t.Background()). - Bold(true) - // Add dropdown indicators - value = "โ—€ " + value + " โ–ถ" - } - - // Create a full-width container for the field - fieldContainer := lipgloss.NewStyle(). - Width(m.width-4). - Background(t.Background()). - Padding(0, 2) - - fieldContent := lipgloss.JoinHorizontal(lipgloss.Left, - labelStyle.Render(label), - valueStyle.Render(value), - ) - - return fieldContainer.Render(fieldContent) - } - - // Helper function to render a toggle field - renderToggleField := func(fieldIndex int, label, value string) string { - active := m.evalState.currentField == fieldIndex - - labelStyle := lipgloss.NewStyle(). - Foreground(t.TextMuted()). - Background(t.Background()). - Width(20). - Align(lipgloss.Right) - - valueStyle := lipgloss.NewStyle(). - Foreground(t.Text()). - Background(t.Background()). - Padding(0, 1) - - if active { - labelStyle = labelStyle.Foreground(t.Primary()).Bold(true) - valueStyle = valueStyle. - Foreground(t.Primary()). - Background(t.Background()). - Bold(true) - } - - // Create a full-width container for the field - fieldContainer := lipgloss.NewStyle(). - Width(m.width-4). - Background(t.Background()). - Padding(0, 2) - - fieldContent := lipgloss.JoinHorizontal(lipgloss.Left, - labelStyle.Render(label), - valueStyle.Render(value), - ) - - return fieldContainer.Render(fieldContent) - } - // Prepare field values - agent := m.evalState.AgentURL - protocol := string(m.evalState.AgentProtocol) - transport := string(m.evalState.AgentTransport) - judge := m.evalState.JudgeModel + agent := state.AgentURL + protocol := string(state.Protocol) + transport := string(state.Transport) + judge := state.JudgeModel deep := "โŒ" if state.DeepTest { deep = "โœ…" @@ -247,7 +171,7 @@ func RenderForm(state *FormState) string { // Helper function to render the start button renderStartButton := func() string { - active := m.evalState.currentField == 5 + active := state.CurrentField == 5 var buttonText string if state.EvalSpinnerActive { diff --git a/packages/tui/internal/tui/utils.go b/packages/tui/internal/tui/utils.go index a19b7282..f43a5c98 100644 --- a/packages/tui/internal/tui/utils.go +++ b/packages/tui/internal/tui/utils.go @@ -41,11 +41,6 @@ type AgentConfig struct { InterviewMode bool `json:"interview_mode"` DeepTestMode bool `json:"deep_test_mode"` ParallelRuns int `json:"parallel_runs"` - -type EvalScenario struct { - Scenario string `json:"scenario"` - ScenarioType ScenarioType `json:"scenario_type"` - ExpectedOutcome string `json:"expected_outcome,omitempty"` } type EvaluationRequest struct {