-
Notifications
You must be signed in to change notification settings - Fork 489
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
Passing Functions as Tools #321
Conversation
6f8cd93
to
93c7a63
Compare
ab402d3
to
7148520
Compare
4e8bd4b
to
4fcdf70
Compare
4fcdf70
to
8ec5123
Compare
|
23548ce
to
1f089f7
Compare
efd3ee9
to
e7bb55f
Compare
65db34a
to
ed3ba8a
Compare
tests/test_client.py
Outdated
# Test missing required fields | ||
incomplete_tool = { | ||
'type': 'function', | ||
'function': {'name': 'test'}, # missing description and parameters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is parameters required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in the previous behavior - no.
But since the last Pydantic PR you have to provide Parameters. Tested out both the OpenAI Python SDK with Ollama and the current published version of ollama-python. I think it might be worthwhile to have the Tool class have optional on all params to make sure it is backwards compatible.
Seems like function calling still works with not a fully-fledged JSON (I'm sure it's flakey but not our job).
Proposed Tool def'n:
class Tool(SubscriptableBaseModel):
type: Optional[Literal['function']] = 'function'
class Function(SubscriptableBaseModel):
name: Optional[str] = None
description: Optional[str] = None
class Parameters(SubscriptableBaseModel):
type: Optional[Literal['object']] = 'object'
required: Optional[Sequence[str]] = None
class Property(SubscriptableBaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
type: Optional[str] = None
description: Optional[str] = None
properties: Optional[Mapping[str, Property]] = None
parameters: Optional[Parameters] = None
function: Optional[Function] = None
e8a4950
to
6d9c156
Compare
3a994b2
to
c5c61a3
Compare
# 1. A parenthetical expression like (integer) - captured in group 1 | ||
# 2. A colon : | ||
# Followed by optional whitespace. Only split on first occurrence. | ||
parts = re.split(r'(?:\(([^)]*)\)|:)\s*', line, maxsplit=1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to avoid regexp when possible since it's hard to grok. In this scenario, a simpler solution would be to split on the mandatory :
than parse the pre-colon and post-colon sections independently. Here's an example that passes your tests
for line in parsed_docstring['args'].splitlines():
pre, _, post = line.partition(':')
if not pre.strip():
continue
if not post.strip() and last_key:
parsed_docstring[last_key] += ' ' + pre
continue
arg_name, _, _ = pre.replace('(', ' ').partition(' ')
last_key = arg_name.strip()
parsed_docstring[last_key] = post.strip()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO @ParthSareen to spin out issue
types = {t.get('type', 'string') for t in v.get('anyOf')} if 'anyOf' in v else {v.get('type', 'string')} | ||
if 'null' in types: | ||
schema['required'].remove(k) | ||
types.discard('null') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If null
is the only type (for some reason), this will be an empty string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is okay, IMO something like:
def (a:None, b:type(None)):
...
is extremely unlikely
* Fix image serialization
* Functions can now be passed as tools
* Functions can now be passed as tools
Features:
.chat(tools=[python_func,...])
🥳Examples to come on follow up for both Pydantic and Functions as Tools