Search before asking
Description
The Python tool schema utilities currently mix several responsibilities in flink_agents.api.tools.utils:
- deriving a Pydantic argument model from a Python callable signature, docstring, defaults, annotations, and injected parameters;
- reconstructing API schema models from serialized JSON Schema;
- converting tool schemas across the Java/Python bridge.
This no longer matches the current API/plan boundary. The API-layer FunctionTool is a declarative descriptor that carries a function reference and injected-argument declarations, while callable introspection and executable ToolMetadata derivation happen in the plan layer. The Java implementation already places the corresponding reflection-based SchemaUtils in plan.tools.
Keeping create_schema_from_function in the API module makes an implementation-specific compilation step look like part of the API contract. It also turns the generic utils.py module into a coupling point for API serialization, plan construction, and runtime bridge code. In addition, Python metadata derivation is partly duplicated between plan.tools.function_tool and runtime.python_java_utils.
Validation gap
The generated Pydantic model is currently used to advertise and serialize the tool input schema, but it is not used to validate the arguments that are actually passed to a Python tool. tool_call_action merges model-provided and framework-injected values and forwards the resulting dictionary to FunctionTool.call, which invokes the underlying callable directly with func(**kwargs).
As a result, the advertised schema and runtime behavior can diverge:
- Pydantic and
Annotated constraints can be shown to the model but are not enforced before invocation;
- nested Pydantic models are not necessarily reconstructed from JSON objects;
- missing, invalid, or unexpected arguments fail through Python binding or user code instead of one deterministic validation path;
- defaults and type coercion depend on Python callable behavior rather than the generated schema;
- Java uses a separate, limited
ToolParameters conversion path, producing different Java/Python semantics.
Schema derivation and runtime validation should therefore be treated as one contract. The same function-schema component that describes model-visible input should also provide validation, normalization, and callable binding for that input.
The desired boundary is:
- keep the tool schema contract and its API-level serialization/deserialization in the API module;
- move Python-callable introspection, schema/metadata derivation, and argument validation/binding to a dedicated plan-layer component;
- let normal plan construction, tool execution, and the cross-language runtime bridge reuse that component;
- validate and normalize model-provided arguments against the same schema advertised to the model before invoking the callable;
- keep framework-injected arguments on a distinct path so they remain hidden from the model, cannot be spoofed, and are not rejected as unexpected model input;
- move Java/Python bridge-specific schema conversion out of the generic API utility where appropriate;
- define consistent Java/Python behavior for required and extra arguments, defaults, coercion, nested values, constraints, and validation errors;
- preserve the existing serialized schema unless an intentional schema change is explicitly agreed.
Acceptance criteria
- The API module no longer owns Python-callable introspection or plan-time metadata derivation.
- Plan construction, tool execution, and the runtime bridge share one function-schema implementation rather than independently deriving or interpreting it.
- Model-provided arguments are validated and normalized against the same schema advertised to the model before the callable is invoked.
- Invalid arguments produce a deterministic tool failure with a useful validation error, and the underlying callable is not invoked.
- Required fields, extra fields, defaults, coercion, nested models, and Pydantic/
Annotated constraints have an explicit, tested policy.
- Injected arguments remain absent from the model-visible schema and cannot be supplied or overridden by the model.
- The
runtime -> plan -> api dependency direction remains intact.
- Java and Python place equivalent schema-derivation responsibilities at consistent architectural layers.
- Focused tests cover schema derivation and runtime invocation, including docstrings,
Annotated metadata and constraints, defaults, missing annotations/descriptions, nested models, invalid/extra arguments, and injected arguments.
- Existing tool schema serialization remains unchanged except for separately agreed fixes.
This is related to #814, which added direct tests for the existing utilities, but addresses the architectural boundary rather than the earlier test-coverage gap.
Are you willing to submit a PR?
Search before asking
Description
The Python tool schema utilities currently mix several responsibilities in
flink_agents.api.tools.utils:This no longer matches the current API/plan boundary. The API-layer
FunctionToolis a declarative descriptor that carries a function reference and injected-argument declarations, while callable introspection and executableToolMetadataderivation happen in the plan layer. The Java implementation already places the corresponding reflection-basedSchemaUtilsinplan.tools.Keeping
create_schema_from_functionin the API module makes an implementation-specific compilation step look like part of the API contract. It also turns the genericutils.pymodule into a coupling point for API serialization, plan construction, and runtime bridge code. In addition, Python metadata derivation is partly duplicated betweenplan.tools.function_toolandruntime.python_java_utils.Validation gap
The generated Pydantic model is currently used to advertise and serialize the tool input schema, but it is not used to validate the arguments that are actually passed to a Python tool.
tool_call_actionmerges model-provided and framework-injected values and forwards the resulting dictionary toFunctionTool.call, which invokes the underlying callable directly withfunc(**kwargs).As a result, the advertised schema and runtime behavior can diverge:
Annotatedconstraints can be shown to the model but are not enforced before invocation;ToolParametersconversion path, producing different Java/Python semantics.Schema derivation and runtime validation should therefore be treated as one contract. The same function-schema component that describes model-visible input should also provide validation, normalization, and callable binding for that input.
The desired boundary is:
Acceptance criteria
Annotatedconstraints have an explicit, tested policy.runtime -> plan -> apidependency direction remains intact.Annotatedmetadata and constraints, defaults, missing annotations/descriptions, nested models, invalid/extra arguments, and injected arguments.This is related to #814, which added direct tests for the existing utilities, but addresses the architectural boundary rather than the earlier test-coverage gap.
Are you willing to submit a PR?