-
Notifications
You must be signed in to change notification settings - Fork 521
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
Add Depends
like behaviour
#32
Conversation
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.
overall LGTM, needs some tests.
pydantic_ai/_system_prompt.py
Outdated
# Annotated with Depends(...) | ||
new_params.append(param) | ||
continue | ||
type_hint = annotated_args[0] |
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.
not sure we need this, I worry it opens up some potentially annoying precedent to allow Annotated[CallContext(..), ...]
.
pydantic_ai/depends.py
Outdated
Callable[P, T], | ||
_InjectWrapper[P, T], | ||
]: | ||
"""Basically the same as `fast_depends.Depends`, just adds a marker indicating that it was called.""" |
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.
"""Basically the same as `fast_depends.Depends`, just adds a marker indicating that it was called.""" | |
"""Basically the same as `fast_depends.inject`, just adds a marker indicating that it was called.""" |
pydantic_ai/depends.py
Outdated
return result | ||
|
||
|
||
def is_injected(func: Callable[..., Any]) -> bool: |
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.
probably want to mark this as private, it shouldn't be in the public API.
presumably we have to relax the signature of |
pydantic_ai/depends.py
Outdated
P = ParamSpec('P') | ||
T = TypeVar('T') | ||
|
||
DependsType = model.Depends |
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.
we should have an __all__
. and Depends
at least should be re-exported from __init__.py
.
Deploying pydantic-ai with
|
Latest commit: |
2b353d5
|
Status: | ✅ Deploy successful! |
Preview URL: | https://af114120.pydantic-ai.pages.dev |
Branch Preview URL: | https://use-fast-depends.pydantic-ai.pages.dev |
432b509
to
652496a
Compare
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.
Overall this looks like a good start, my main concern that there's a lot more work to do.
I also wonder if we should add a way to customise Deps
similar to dependency_provider.scope
but specifically for accessing them from CallContext
. Something like
with pydantic_ai.override_deps(MyAgent, custom_deps):
...
I think next steps (in order of priority):
- make sure
Depends
is working in system prompts, retrievers and result validation - add tests
- get the API right so stuff we don't want to expose is private
- add thorough docstrings
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 would suggest we move /_depends
to /depends
to make it public, then only export Depends
form the root __init__.py
Making this file public should also make the API documentation easier.
@@ -0,0 +1,118 @@ | |||
import inspect |
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.
one line docstring would be nice.
use_cache: bool = True, | ||
is_sync: bool | None = None, | ||
) -> CallModel[P, T]: | ||
name = getattr(call, '__name__', type(call).__name__) |
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 we assume elsewhere that call.__name__
is always available for callables.
for arg in annotated_args[1:]: | ||
if isinstance(arg, DependsType): | ||
if dep: | ||
raise ValueError(f'Cannot specify multiple `Depends` arguments for `{param_name}`!') |
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.
We should probably use our UserError
exception, or remove it and use plain ValueError
everywhere.
*, | ||
use_cache: bool = True, | ||
is_sync: bool | None = None, | ||
) -> CallModel[P, T]: |
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.
quick docstring even if this is private.
# Define the response type | ||
@dataclass | ||
class ReviewAnalysisResponse: | ||
score: int # Between -5 (most negative) and +5 (most positive) |
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.
constraint, also use Field
to add a description to help the LLM.
@dataclass | ||
class ReviewAnalysisResponse: | ||
score: int # Between -5 (most negative) and +5 (most positive) | ||
tags: list[ReviewTag] # Extracted tags from the review |
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.
description again.
tags: list[ReviewTag] # Extracted tags from the review | ||
|
||
|
||
Response = ReviewAnalysisResponse |
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.
do we need this?
agent = Agent[None, Response]( | ||
result_type=Response, | ||
retries=2, | ||
model=GeminiModel('gemini-1.5-flash', api_key='<OMITTED>'), |
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.
agent = Agent[None, Response]( | |
result_type=Response, | |
retries=2, | |
model=GeminiModel('gemini-1.5-flash', api_key='<OMITTED>'), | |
agent = Agent( | |
'gemini-1.5-flash' | |
result_type=Response, | |
retries=2, |
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.
The principle looks good, I think we need do a better job of separating the production code from the evals code.
Depends
like behaviour
Closing in favor of alternative approaches, just too much to add here |
Can't believe it but it looks like this passes all tests and type-checking.
We'd need to do the same for the the other kinds of things that can be done with decorators but this seems pretty nice. Note that this requires
CallContext
to work (can't use bareDeps
), so I think it's probably best to keep that around.