Captures the schema for a python function, in preparation for sending it to an LLM as a tool.
| 21 | |
| 22 | @dataclass |
| 23 | class FuncSchema: |
| 24 | """ |
| 25 | Captures the schema for a python function, in preparation for sending it to an LLM as a tool. |
| 26 | """ |
| 27 | |
| 28 | name: str |
| 29 | """The name of the function.""" |
| 30 | description: str | None |
| 31 | """The description of the function.""" |
| 32 | params_pydantic_model: type[BaseModel] |
| 33 | """A Pydantic model that represents the function's parameters.""" |
| 34 | params_json_schema: dict[str, Any] |
| 35 | """The JSON schema for the function's parameters, derived from the Pydantic model.""" |
| 36 | signature: inspect.Signature |
| 37 | """The signature of the function.""" |
| 38 | takes_context: bool = False |
| 39 | """Whether the function takes a RunContextWrapper argument (must be the first argument).""" |
| 40 | strict_json_schema: bool = True |
| 41 | """Whether the JSON schema is in strict mode. We **strongly** recommend setting this to True, |
| 42 | as it increases the likelihood of correct JSON input.""" |
| 43 | |
| 44 | def to_call_args(self, data: BaseModel) -> tuple[list[Any], dict[str, Any]]: |
| 45 | """ |
| 46 | Converts validated data from the Pydantic model into (args, kwargs), suitable for calling |
| 47 | the original function. |
| 48 | """ |
| 49 | positional_args: list[Any] = [] |
| 50 | keyword_args: dict[str, Any] = {} |
| 51 | seen_var_positional = False |
| 52 | |
| 53 | # Use enumerate() so we can skip the first parameter if it's context. |
| 54 | for idx, (name, param) in enumerate(self.signature.parameters.items()): |
| 55 | # If the function takes a RunContextWrapper and this is the first parameter, skip it. |
| 56 | if self.takes_context and idx == 0: |
| 57 | continue |
| 58 | |
| 59 | value = getattr(data, name, None) |
| 60 | if param.kind == param.VAR_POSITIONAL: |
| 61 | # e.g. *args: extend positional args and mark that *args is now seen |
| 62 | positional_args.extend(value or []) |
| 63 | seen_var_positional = True |
| 64 | elif param.kind == param.VAR_KEYWORD: |
| 65 | # e.g. **kwargs handling |
| 66 | keyword_args.update(value or {}) |
| 67 | elif param.kind in (param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD): |
| 68 | # Before *args, add to positional args. After *args, add to keyword args. |
| 69 | if not seen_var_positional: |
| 70 | positional_args.append(value) |
| 71 | else: |
| 72 | keyword_args[name] = value |
| 73 | else: |
| 74 | # For KEYWORD_ONLY parameters, always use keyword args. |
| 75 | keyword_args[name] = value |
| 76 | return positional_args, keyword_args |
| 77 | |
| 78 | |
| 79 | @dataclass |