-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat(api): Implement connector support via static configuration #4263
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
base: main
Are you sure you want to change the base?
Conversation
174b8e9 to
5331e80
Compare
cdoern
left a comment
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 comment so far, the implementation looks good especially compared to prompts which follows a similar structure.
| Registry, | ||
| ToolDef, | ||
| ) | ||
| from llama_stack_api.common.errors import ( |
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.
include this in the from llama_stack_api import. or If we missed these please add them to llama_stack_api's __init__.py thanks!
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.
updated, thanks!
4fa016e to
01a76e0
Compare
✱ Stainless preview buildsThis PR will update the Edit this comment to update it. It will appear in the SDK's changelogs. ✅ llama-stack-client-node studio · code · diff
✅ llama-stack-client-python studio · code · diff
✅ llama-stack-client-go studio · code · diff
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push. |
src/llama_stack/providers/inline/agents/meta_reference/responses/streaming.py
Outdated
Show resolved
Hide resolved
|
This pull request has merge conflicts that must be resolved before it can be merged. @jaideepr97 please rebase it. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork |
01a76e0 to
e6633bb
Compare
f596311 to
eb67320
Compare
|
added unit tests for this feature. Looked into adding an integration test as well, but ran into a chicken and egg problem with the connector needing to be configured in the stack at boot time, but the mcp servers will need to be spun up at runtime with a port not known beforehand This feature is currently incomplete with only read operations supported for connectors. IMO, we should wait until add/delete endpoints are available (#4239 ) for connectors so that the feature is truly complete and it is easier to write integration tests when connectors can be registered/unregistered on the fly |
bf737bc to
78cdbfe
Compare
e58b376 to
3cb63dc
Compare
ashwinb
left a comment
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.
lgtm
mattf
left a comment
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.
question about the future use of connectors: should each responses provider need to handle connectors or should the inference router convert connector_id to connector_url before dispatching to providers?
| # Upsert: always write (idempotent for boot-time registration) | ||
| if await self.kvstore.get(self._get_key(connector_id)): | ||
| logger.debug(f"Found existing connector: {connector_id}, overwritting it") | ||
| await self.kvstore.set(self._get_key(connector_id), connector.model_dump_json()) |
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.
this is unconditional overwriting. idempotent would be to accept if the value is the same and reject otherwise.
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.
updated
| stored = await mock_kvstore.get(f"{KEY_PREFIX}my-mcp") | ||
| assert stored is not None | ||
|
|
||
| async def test_register_connector_idempotent(self, connector_service, mock_kvstore): |
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.
to validate idempotency, you also need a negative test, one where the id is the same but the values are different and the service rejects the registration.
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.
added
client-sdks/stainless/openapi.yml
Outdated
| source: | ||
| $ref: '#/components/schemas/ConnectorSource' | ||
| description: "How the connector was registered: 'config' (from run.yaml) or 'api' (via API call)" | ||
| default: config |
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.
why expose this detail?
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.
this field was added to be able to differentiate between stale config connectors and connectors that were registered via api
updated to exclude from schema
3cb63dc to
3be402c
Compare
I'm not sure at the moment since we only have 1 agents/responses provider (do we plan to have more?) |
3be402c to
f834680
Compare
cdoern
left a comment
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.
thanks for all of the back and forth on this one!
|
@mattf PTAL, this is the last PR linked to an issue in the 0.4.0 milestone |
docs/static/llama-stack-spec.yaml
Outdated
| ConnectorSource: | ||
| type: string | ||
| enum: | ||
| - config | ||
| - api | ||
| title: ConnectorSource | ||
| description: Source of connector registration. |
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.
this shouldn't be exposed
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.
updated, should be fixed now
| """Register a new connector (idempotent - updates if already exists).""" | ||
|
|
||
| connector = Connector( | ||
| connector_id=connector_id, | ||
| connector_type=connector_type, | ||
| source=source, | ||
| url=url, | ||
| server_label=server_label, | ||
| server_name=server_name, | ||
| server_description=server_description, | ||
| ) | ||
|
|
||
| key = self._get_key(connector_id) | ||
| existing_connector_json = await self.kvstore.get(key) | ||
|
|
||
| if existing_connector_json: | ||
| existing_connector = Connector.model_validate_json(existing_connector_json) | ||
|
|
||
| # Only overwrite if the connector is an exact match; otherwise log and keep existing. | ||
| if existing_connector.model_dump() != connector.model_dump(): | ||
| logger.info( | ||
| "Connector %s already exists with different configuration; skipping overwrite", | ||
| connector_id, | ||
| ) | ||
| return existing_connector | ||
|
|
||
| logger.debug("Connector %s already exists and matches; overwriting with same value", connector_id) | ||
|
|
||
| # Persist full connector, including source (Field is excluded from schema/dumps by default). | ||
| connector_payload = connector.model_dump() | ||
| connector_payload["source"] = connector.source | ||
| await self.kvstore.set(key, json.dumps(connector_payload)) | ||
|
|
||
| return connector |
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.
"idempotent - updates if already exists"
please self-review this code.
consider -
- why update if the values are the same?
- how will a user know their register attempt failed?
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.
here's the logic I ended up with after another look:
- config is the source of truth at boot time. config connectors can also never be updated, only registered. Therefore, config based connectors are allowed overwrites at registration time, so stack admins can take deliberate action to "update" any previously registered connector by updating the config and restarting the server. If the values are the same then it is a no-op
- if a new connector has a different source (api) and has conflicts with an existing connector, then the registration fails, since that is technically an update operation and should happen via the update endpoint (once it is implemented in Add /admin level Create/Update/Delete endpoints for the connectors API #4239 ) and not at registration time
as for
how will a user know their register attempt failed?
the user will always be a stack admin since only they can register new connectors (via config or /admin api), so I left it as a log statement that they can check instead of raising an error
Signed-off-by: Jaideep Rao <jrao@redhat.com>
Signed-off-by: Jaideep Rao <jrao@redhat.com>
Signed-off-by: Jaideep Rao <jrao@redhat.com>
Signed-off-by: Jaideep Rao <jrao@redhat.com>
…andle stale config connectors Signed-off-by: Jaideep Rao <jrao@redhat.com>
Signed-off-by: Jaideep Rao <jrao@redhat.com>
… from openapi schema Signed-off-by: Jaideep Rao <jrao@redhat.com>
Signed-off-by: Jaideep Rao <jrao@redhat.com>
Signed-off-by: Jaideep Rao <jrao@redhat.com>
e65d620 to
9f51de0
Compare
mattf
left a comment
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.
there's a debate about how to handle resources in config vs api. to be consistent, you should not include the source as an input param and resources defined in config can be unregistered + registered via api.
an admin for the service may not be the same as the service operator.
@ashwinb felt connectors should not be considered to be resources since they have no provider and are not routable and suggested to look at them as simple objects instead Not sure how that informs the debate or connectors' subjectivity to it |
|
@mattf providing some more context/thoughts/questions here based on further looking at what exists -
I found
alternatively, if connectors should stick with the pattern of not differentiating between
option 1 assumes that the config is meant to be an accurate reflection of the state of things at any given time as opposed to just a point of initialization. Maybe that is a key point that will decide what approach should be followed If I have misunderstood, and you only meant to remove source from the function signature for
if we extend this to all objects, this would still be possible for connectors once API operations (other than read) are implemented in #4239 , as long as it is done in 2 steps - unregister.+ register. The only API operation I have blocked here is re-registering an existing connector via API in a single action.
That's fair - users registering connectors via API would not see the log statement. I think that should be handled in the follow up PR when that API is the focus. This PR focuses on the config based workflow, for which the logging is enough since only stack admins can set the config and see the logs. How does that sound? All of this is also encapsulated by the open question about whether non-resources are expected to follow the same standards and patterns as resources |
What does this PR do?
Implements support for configuring static connectors in the stack via run.yaml. Major features of this PR:
connectorsin the run.yamlserver_urlorconnector_idand handles resolutionExamples:
run.yaml config:
API requests:
Client side usage example:
output:
Closes #4186 and #4061 (partially)
Test Plan
pending
NOTE: this PR builds on top of #4258 therefore it also contains changes from it. This PR should only be reviewed after #4258