fix tests

This commit is contained in:
Swifty
2026-02-11 14:04:19 +01:00
parent 35825a618d
commit 277f435c39
3 changed files with 80 additions and 16 deletions

View File

@@ -166,24 +166,32 @@ class RunBlockTool(BaseTool):
)
# Get block schemas for details/validation
input_schema: dict[str, Any] = {}
output_schema: dict[str, Any] = {}
try:
input_schema = block.input_schema.jsonschema()
input_schema: dict[str, Any] = block.input_schema.jsonschema()
except Exception as e:
logger.debug(
logger.warning(
"Failed to generate input schema for block %s: %s",
block_id,
e,
)
return ErrorResponse(
message=f"Block '{block.name}' has an invalid input schema",
error=str(e),
session_id=session_id,
)
try:
output_schema = block.output_schema.jsonschema()
output_schema: dict[str, Any] = block.output_schema.jsonschema()
except Exception as e:
logger.debug(
logger.warning(
"Failed to generate output schema for block %s: %s",
block_id,
e,
)
return ErrorResponse(
message=f"Block '{block.name}' has an invalid output schema",
error=str(e),
session_id=session_id,
)
if missing_credentials:
# Return setup requirements response with missing credentials
@@ -217,17 +225,17 @@ class RunBlockTool(BaseTool):
graph_version=None,
)
# Check if this is a first attempt (no input data provided for a block that has inputs)
# Check if this is a first attempt (required inputs missing)
# Return block details so user can see what inputs are needed
input_properties = input_schema.get("properties", {})
credentials_fields = set(block.input_schema.get_credentials_fields().keys())
non_credential_properties = {
k: v for k, v in input_properties.items() if k not in credentials_fields
}
required_keys = set(input_schema.get("required", []))
required_non_credential_keys = required_keys - credentials_fields
provided_input_keys = set(input_data.keys()) - credentials_fields
# If block has non-credential inputs but none were provided, show details first
if non_credential_properties and not provided_input_keys:
# Show details when there are required non-credential inputs and none are provided
if required_non_credential_keys and not (
required_non_credential_keys & provided_input_keys
):
# Get credentials info for the response
credentials_meta = []
for field_name, cred_meta in matched_credentials.items():

View File

@@ -7,6 +7,7 @@ import pytest
from backend.api.features.chat.tools.models import BlockDetailsResponse
from backend.api.features.chat.tools.run_block import RunBlockTool
from backend.data.block import BlockType
from backend.data.model import CredentialsMetaInput
from ._test_data import make_session
@@ -65,7 +66,7 @@ async def test_run_block_returns_details_when_no_input_provided():
# Mock credentials check to return no missing credentials
with patch.object(
RunBlockTool,
"_check_block_credentials",
"_resolve_block_credentials",
new_callable=AsyncMock,
return_value=({}, []), # (matched_credentials, missing_credentials)
):
@@ -123,9 +124,19 @@ async def test_run_block_returns_details_when_only_credentials_provided():
):
with patch.object(
RunBlockTool,
"_check_block_credentials",
"_resolve_block_credentials",
new_callable=AsyncMock,
return_value=({"credentials": MagicMock()}, []),
return_value=(
{
"credentials": CredentialsMetaInput(
id="cred-id",
provider="test_provider",
type="api_key",
title="Test Credential",
)
},
[],
),
):
tool = RunBlockTool()
response = await tool._execute(

View File

@@ -10,6 +10,22 @@ import {
import type { ToolUIPart } from "ai";
import { OrbitLoader } from "../../components/OrbitLoader/OrbitLoader";
/** Block details returned on first run_block attempt (before input_data provided). */
export interface BlockDetailsResponse {
type: typeof ResponseType.block_details;
message: string;
session_id?: string | null;
block: {
id: string;
name: string;
description: string;
inputs: Record<string, unknown>;
outputs: Record<string, unknown>;
credentials: unknown[];
};
user_authenticated: boolean;
}
export interface RunBlockInput {
block_id?: string;
block_name?: string;
@@ -18,11 +34,13 @@ export interface RunBlockInput {
export type RunBlockToolOutput =
| SetupRequirementsResponse
| BlockDetailsResponse
| BlockOutputResponse
| ErrorResponse;
const RUN_BLOCK_OUTPUT_TYPES = new Set<string>([
ResponseType.setup_requirements,
ResponseType.block_details,
ResponseType.block_output,
ResponseType.error,
]);
@@ -36,6 +54,15 @@ export function isRunBlockSetupRequirementsOutput(
);
}
export function isRunBlockDetailsOutput(
output: RunBlockToolOutput,
): output is BlockDetailsResponse {
return (
output.type === ResponseType.block_details ||
("block" in output && typeof output.block === "object")
);
}
export function isRunBlockBlockOutput(
output: RunBlockToolOutput,
): output is BlockOutputResponse {
@@ -65,6 +92,7 @@ function parseOutput(output: unknown): RunBlockToolOutput | null {
return output as RunBlockToolOutput;
}
if ("block_id" in output) return output as BlockOutputResponse;
if ("block" in output) return output as BlockDetailsResponse;
if ("setup_info" in output) return output as SetupRequirementsResponse;
if ("error" in output || "details" in output)
return output as ErrorResponse;
@@ -102,6 +130,8 @@ export function getAnimationText(part: {
const output = parseOutput(part.output);
if (!output) return `Running${blockText}`;
if (isRunBlockBlockOutput(output)) return `Ran "${output.block_name}"`;
if (isRunBlockDetailsOutput(output))
return `Details for "${output.block.name}"`;
if (isRunBlockSetupRequirementsOutput(output)) {
return `Setup needed for "${output.setup_info.agent_name}"`;
}
@@ -165,6 +195,21 @@ export function getAccordionMeta(output: RunBlockToolOutput): {
};
}
if (isRunBlockDetailsOutput(output)) {
const inputKeys = Object.keys(
(output.block.inputs as { properties?: Record<string, unknown> })
?.properties ?? {},
);
return {
icon,
title: output.block.name,
description:
inputKeys.length > 0
? `${inputKeys.length} input field${inputKeys.length === 1 ? "" : "s"} available`
: output.message,
};
}
if (isRunBlockSetupRequirementsOutput(output)) {
const missingCredsCount = Object.keys(
(output.setup_info.user_readiness?.missing_credentials ?? {}) as Record<