-
Notifications
You must be signed in to change notification settings - Fork 3.7k
feat: support self/cls in function_tool for class methods #2734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
008294b
73b9a9f
2e85520
22f1f83
98ba075
f3f480c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -287,6 +287,9 @@ def function_schema( | |
| takes_context = False | ||
| filtered_params = [] | ||
|
|
||
| # Track whether the first real (non-self/cls) parameter has been processed for context check | ||
| self_or_cls_skipped = False | ||
|
|
||
| if params: | ||
| first_name, first_param = params[0] | ||
| # Prefer the evaluated type hint if available | ||
|
|
@@ -297,15 +300,24 @@ def function_schema( | |
| takes_context = True # Mark that the function takes context | ||
| else: | ||
| filtered_params.append((first_name, first_param)) | ||
| elif first_name in ("self", "cls"): | ||
| self_or_cls_skipped = True # Skip bound method receiver parameter | ||
| else: | ||
| filtered_params.append((first_name, first_param)) | ||
|
|
||
| # For parameters other than the first, raise error if any use RunContextWrapper or ToolContext. | ||
| for name, param in params[1:]: | ||
| # For parameters other than the first, raise error if any use RunContextWrapper or ToolContext | ||
| # (unless self/cls was skipped, in which case ONLY the param immediately after self/cls is | ||
| # treated as the effective first param). | ||
| for idx, (name, param) in enumerate(params[1:]): | ||
| ann = type_hints.get(name, param.annotation) | ||
| if ann != inspect._empty: | ||
| origin = get_origin(ann) or ann | ||
| if origin is RunContextWrapper or origin is ToolContext: | ||
| if self_or_cls_skipped and not takes_context and idx == 0: | ||
| # self/cls was the first param and this is immediately after it | ||
| takes_context = True | ||
| self_or_cls_skipped = False | ||
| continue | ||
| raise UserError( | ||
| f"RunContextWrapper/ToolContext param found at non-first position in function" | ||
| f" {func.__name__}" | ||
|
|
@@ -409,14 +421,22 @@ def function_schema( | |
| if strict_json_schema: | ||
| json_schema = ensure_strict_json_schema(json_schema) | ||
|
|
||
| # 5. Return as a FuncSchema dataclass | ||
| # 5. Build a signature that excludes self/cls so to_call_args iterates | ||
| # only the parameters the caller actually needs to pass. | ||
| if self_or_cls_skipped or (takes_context and params and params[0][0] in ("self", "cls")): | ||
| remaining = [p for name, p in params if name not in ("self", "cls")] | ||
|
seratch marked this conversation as resolved.
Outdated
|
||
| call_sig = sig.replace(parameters=remaining) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This filter removes every parameter named Useful? React with 👍 / 👎. |
||
| else: | ||
| call_sig = sig | ||
|
|
||
| # 6. Return as a FuncSchema dataclass | ||
| return FuncSchema( | ||
| name=func_name, | ||
| # Ensure description_override takes precedence even if docstring info is disabled. | ||
| description=description_override or (doc_info.description if doc_info else None), | ||
| params_pydantic_model=dynamic_model, | ||
| params_json_schema=json_schema, | ||
| signature=sig, | ||
| signature=call_sig, | ||
| takes_context=takes_context, | ||
| strict_json_schema=strict_json_schema, | ||
| ) | ||
Uh oh!
There was an error while loading. Please reload this page.