mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-07 07:55:16 +02:00
feat: render QA in UI
This commit is contained in:
parent
c8742dbdc0
commit
ef080d57c8
7 changed files with 34 additions and 80 deletions
|
|
@ -674,6 +674,7 @@ async def get_workflow_run(
|
||||||
"gathered_context": run.gathered_context,
|
"gathered_context": run.gathered_context,
|
||||||
"call_type": run.call_type,
|
"call_type": run.call_type,
|
||||||
"logs": run.logs,
|
"logs": run.logs,
|
||||||
|
"annotations": run.annotations,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,3 +21,4 @@ class WorkflowRunResponseSchema(BaseModel):
|
||||||
gathered_context: dict | None = None
|
gathered_context: dict | None = None
|
||||||
call_type: CallType
|
call_type: CallType
|
||||||
logs: Dict[str, Any] | None = None
|
logs: Dict[str, Any] | None = None
|
||||||
|
annotations: Dict[str, Any] | None = None
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"""LLM configuration resolution and token usage accumulation."""
|
"""LLM configuration resolution and token usage accumulation."""
|
||||||
|
|
||||||
|
from api.constants import MPS_API_URL
|
||||||
from api.db import db_client
|
from api.db import db_client
|
||||||
from api.db.models import WorkflowRunModel
|
from api.db.models import WorkflowRunModel
|
||||||
|
|
||||||
|
|
@ -14,6 +15,8 @@ def _provider_base_url(provider: str | None, endpoint: str = "") -> str | None:
|
||||||
return "https://generativelanguage.googleapis.com/v1beta/openai/"
|
return "https://generativelanguage.googleapis.com/v1beta/openai/"
|
||||||
if provider == "azure":
|
if provider == "azure":
|
||||||
return endpoint or None
|
return endpoint or None
|
||||||
|
if provider == "dograh":
|
||||||
|
return f"{MPS_API_URL}/api/v1/llm"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,15 +98,6 @@ async def _run_qa_nodes(
|
||||||
logger.error(f"QA analysis failed for node '{node_name}': {e}")
|
logger.error(f"QA analysis failed for node '{node_name}': {e}")
|
||||||
results[f"qa_{node_id}"] = {"error": str(e)}
|
results[f"qa_{node_id}"] = {"error": str(e)}
|
||||||
|
|
||||||
# Collect all unique tags across all QA node results for top-level filtering
|
|
||||||
all_tags: set[str] = set()
|
|
||||||
for result in results.values():
|
|
||||||
for node_result in result.get("node_results", {}).values():
|
|
||||||
if isinstance(node_result, dict):
|
|
||||||
all_tags.update(node_result.get("tags", []))
|
|
||||||
if all_tags:
|
|
||||||
results["tags"] = sorted(all_tags)
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -219,15 +210,27 @@ async def run_integrations_post_workflow_run(_ctx, workflow_run_id: int):
|
||||||
)
|
)
|
||||||
|
|
||||||
if qa_results:
|
if qa_results:
|
||||||
await db_client.update_workflow_run(
|
|
||||||
workflow_run_id, annotations=qa_results
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add QA token usage to workflow run's usage_info
|
# Add QA token usage to workflow run's usage_info
|
||||||
await _update_usage_info_with_qa_tokens(
|
await _update_usage_info_with_qa_tokens(
|
||||||
workflow_run_id, workflow_run, qa_results
|
workflow_run_id, workflow_run, qa_results
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Collect unique tags across all QA node results for top-level filtering
|
||||||
|
all_tags: set[str] = set()
|
||||||
|
for qa_key, qa_result in qa_results.items():
|
||||||
|
for node_result in qa_result.get("node_results", {}).values():
|
||||||
|
for tag in node_result.get("tags", []):
|
||||||
|
if isinstance(tag, str):
|
||||||
|
all_tags.add(tag)
|
||||||
|
elif isinstance(tag, dict) and "tag" in tag:
|
||||||
|
all_tags.add(tag["tag"])
|
||||||
|
if all_tags:
|
||||||
|
qa_results["tags"] = sorted(all_tags)
|
||||||
|
|
||||||
|
await db_client.update_workflow_run(
|
||||||
|
workflow_run_id, annotations=qa_results
|
||||||
|
)
|
||||||
|
|
||||||
# Re-fetch workflow_run to get updated annotations
|
# Re-fetch workflow_run to get updated annotations
|
||||||
workflow_run, _ = await db_client.get_workflow_run_with_context(
|
workflow_run, _ = await db_client.get_workflow_run_with_context(
|
||||||
workflow_run_id
|
workflow_run_id
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ interface WorkflowRunResponse {
|
||||||
initial_context: Record<string, string | number | boolean | object> | null;
|
initial_context: Record<string, string | number | boolean | object> | null;
|
||||||
gathered_context: Record<string, string | number | boolean | object> | null;
|
gathered_context: Record<string, string | number | boolean | object> | null;
|
||||||
logs: WorkflowRunLogs | null;
|
logs: WorkflowRunLogs | null;
|
||||||
|
annotations: Record<string, unknown> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ContextDisplay({ title, context }: { title: string; context: Record<string, string | number | boolean | object> | null }) {
|
function ContextDisplay({ title, context }: { title: string; context: Record<string, string | number | boolean | object> | null }) {
|
||||||
|
|
@ -107,6 +108,7 @@ export default function WorkflowRunPage() {
|
||||||
initial_context: response.data?.initial_context as Record<string, string> | null ?? null,
|
initial_context: response.data?.initial_context as Record<string, string> | null ?? null,
|
||||||
gathered_context: response.data?.gathered_context as Record<string, string> | null ?? null,
|
gathered_context: response.data?.gathered_context as Record<string, string> | null ?? null,
|
||||||
logs: response.data?.logs as WorkflowRunLogs | null ?? null,
|
logs: response.data?.logs as WorkflowRunLogs | null ?? null,
|
||||||
|
annotations: response.data?.annotations as Record<string, unknown> | null ?? null,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
fetchWorkflowRun();
|
fetchWorkflowRun();
|
||||||
|
|
@ -237,6 +239,13 @@ export default function WorkflowRunPage() {
|
||||||
context={workflowRun?.gathered_context}
|
context={workflowRun?.gathered_context}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{workflowRun?.annotations && Object.keys(workflowRun.annotations).length > 0 && (
|
||||||
|
<ContextDisplay
|
||||||
|
title="QA Results"
|
||||||
|
context={workflowRun.annotations as Record<string, string | number | boolean | object>}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -70,16 +70,6 @@ export type AccessTokenResponse = {
|
||||||
connection_id: string;
|
connection_id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AdminCommentRequest = {
|
|
||||||
admin_comment: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AdminCommentResponse = {
|
|
||||||
success: boolean;
|
|
||||||
admin_comment: string;
|
|
||||||
admin_comment_ts: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AuthResponse = {
|
export type AuthResponse = {
|
||||||
token: string;
|
token: string;
|
||||||
user: UserResponse;
|
user: UserResponse;
|
||||||
|
|
@ -858,8 +848,6 @@ export type SuperuserWorkflowRunResponse = {
|
||||||
gathered_context: {
|
gathered_context: {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
} | null;
|
} | null;
|
||||||
admin_comment: string | null;
|
|
||||||
admin_comment_ts: string | null;
|
|
||||||
created_at: string;
|
created_at: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1347,6 +1335,9 @@ export type WorkflowRunResponseSchema = {
|
||||||
logs?: {
|
logs?: {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
} | null;
|
} | null;
|
||||||
|
annotations?: {
|
||||||
|
[key: string]: unknown;
|
||||||
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkflowRunUsageResponse = {
|
export type WorkflowRunUsageResponse = {
|
||||||
|
|
@ -1836,41 +1827,6 @@ export type GetWorkflowRunsApiV1SuperuserWorkflowRunsGetResponses = {
|
||||||
|
|
||||||
export type GetWorkflowRunsApiV1SuperuserWorkflowRunsGetResponse = GetWorkflowRunsApiV1SuperuserWorkflowRunsGetResponses[keyof GetWorkflowRunsApiV1SuperuserWorkflowRunsGetResponses];
|
export type GetWorkflowRunsApiV1SuperuserWorkflowRunsGetResponse = GetWorkflowRunsApiV1SuperuserWorkflowRunsGetResponses[keyof GetWorkflowRunsApiV1SuperuserWorkflowRunsGetResponses];
|
||||||
|
|
||||||
export type SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostData = {
|
|
||||||
body: AdminCommentRequest;
|
|
||||||
headers?: {
|
|
||||||
authorization?: string | null;
|
|
||||||
'X-API-Key'?: string | null;
|
|
||||||
};
|
|
||||||
path: {
|
|
||||||
run_id: number;
|
|
||||||
};
|
|
||||||
query?: never;
|
|
||||||
url: '/api/v1/superuser/workflow-runs/{run_id}/comment';
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostErrors = {
|
|
||||||
/**
|
|
||||||
* Not found
|
|
||||||
*/
|
|
||||||
404: unknown;
|
|
||||||
/**
|
|
||||||
* Validation Error
|
|
||||||
*/
|
|
||||||
422: HttpValidationError;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostError = SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostErrors[keyof SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostErrors];
|
|
||||||
|
|
||||||
export type SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostResponses = {
|
|
||||||
/**
|
|
||||||
* Successful Response
|
|
||||||
*/
|
|
||||||
200: AdminCommentResponse;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostResponse = SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostResponses[keyof SetAdminCommentApiV1SuperuserWorkflowRunsRunIdCommentPostResponses];
|
|
||||||
|
|
||||||
export type ValidateWorkflowApiV1WorkflowWorkflowIdValidatePostData = {
|
export type ValidateWorkflowApiV1WorkflowWorkflowIdValidatePostData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
headers?: {
|
headers?: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue