diff --git a/content/develop/ai/_index.md b/content/develop/ai/_index.md index 98cd9b06de..0cf3f94a9b 100644 --- a/content/develop/ai/_index.md +++ b/content/develop/ai/_index.md @@ -66,11 +66,12 @@ AI agents are autonomous systems that combine LLMs with memory, tools, and plann ### Context Engine services -The [Context Engine]({{< relref "/develop/ai/context-engine" >}}) (Redis Iris) includes four services, available fully managed on Redis Cloud or self-managed on your own infrastructure: +The [Context Engine]({{< relref "/develop/ai/context-engine" >}}) (Redis Iris) includes these services, available fully managed on Redis Cloud or self-managed on your own infrastructure: - [LangCache]({{< relref "/develop/ai/context-engine/langcache" >}}): Semantic caching that reduces LLM API costs and improves response times by reusing cached responses for similar queries. - [Redis Agent Memory]({{< relref "/develop/ai/context-engine/agent-memory" >}}): Two-tier persistent memory (session and long-term) for agents, available through Python and TypeScript SDKs and a REST API. - [Context Gateway]({{< relref "/develop/ai/context-engine/context-gateway" >}}): Gives agents one Model Context Protocol (MCP) endpoint for the tools of any MCP server, and serves your Redis data as tools with Context Surfaces. +- [Intent Router]({{< relref "/develop/ai/context-engine/intent-router" >}}): Classifies a user's message into an intent you define and returns the target to dispatch to, with no LLM call. Self-managed on Kubernetes. - [Data Integration]({{< relref "/develop/ai/context-engine/data-integration" >}}): Keeps your Redis Cloud database in sync with relational databases in near real time using Change Data Capture. ## How to's @@ -140,6 +141,7 @@ The Context Engine provides managed services for agent memory and data access. * [Get started with LangCache]({{< relref "/develop/ai/context-engine/langcache" >}}): Add semantic caching to reduce LLM costs in minutes. * [Get started with Agent Memory]({{< relref "/develop/ai/context-engine/agent-memory" >}}): Add persistent two-tier memory to any agent using the REST API. * [Get started with Context Gateway]({{< relref "/develop/ai/context-engine/context-gateway" >}}): Give agents one MCP endpoint for the tools of your MCP servers and your Redis data. +* [Get started with Intent Router]({{< relref "/develop/ai/context-engine/intent-router" >}}): Route each request to the right tool or agent by matching it to intents you define. * [Get started with Data Integration]({{< relref "/develop/ai/context-engine/data-integration" >}}): Keep Redis in sync with your primary database so agents always have fresh data. ## Tutorials diff --git a/content/develop/ai/context-engine/intent-router/_index.md b/content/develop/ai/context-engine/intent-router/_index.md index 7b75f5c072..6d95970d9c 100644 --- a/content/develop/ai/context-engine/intent-router/_index.md +++ b/content/develop/ai/context-engine/intent-router/_index.md @@ -36,7 +36,7 @@ For example, a support application can route "where is my package?" to a `track_ - **Tunable strictness:** Set a minimum score on the Router, on each intent, or on a single request. - **Safe updates:** Replacing an intent never leaves it empty while queries are in flight, and conditional writes prevent lost updates. - **Governed access:** Each Router is its own authorization boundary, with separate `read`, `write`, and `full` grants. -- **Flexible integration:** Use Python, TypeScript, the `iris` command-line interface (CLI), or the Hypertext Transfer Protocol (HTTP) API. +- **Flexible integration:** Use Python, the `iris` command-line interface (CLI), or the Hypertext Transfer Protocol (HTTP) API. ## Choose how to use it @@ -60,9 +60,9 @@ pip install redis-iris Set `IRIS_INTENT_ROUTER_URL`, `IRIS_INTENT_ROUTER_ID`, and `IRIS_INTENT_ROUTER_API_KEY` as shown in the [quickstart](/content/develop/ai/context-engine/intent-router/quickstart.md#save-connection-values), connect, and define intents: -{{< clients-example set="iris_intent_router_quickstart" step="intents-create" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_quickstart" step="intents-create" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} -The [redis-iris package](https://pypi.org/project/redis-iris/) covers matching, intent authoring, and Router administration in Python and the CLI. For TypeScript, the generated client [@redis-iris/intent-router](https://www.npmjs.com/package/@redis-iris/intent-router) covers the data plane and the control plane. See the [developer guide](/content/develop/ai/context-engine/intent-router/developer-guide.md) to choose a client. +The [redis-iris package](https://pypi.org/project/redis-iris/) covers matching, intent authoring, and Router administration in Python and the CLI. See the [developer guide](/content/develop/ai/context-engine/intent-router/developer-guide.md) to choose a client. ## Get started diff --git a/content/develop/ai/context-engine/intent-router/concepts.md b/content/develop/ai/context-engine/intent-router/concepts.md index a0c641cc0e..4c168444ec 100644 --- a/content/develop/ai/context-engine/intent-router/concepts.md +++ b/content/develop/ai/context-engine/intent-router/concepts.md @@ -98,7 +98,7 @@ Two credentials guard the service: - An **admin token** manages Routers on the control plane. Platform teams hold it. - An **agent key** authenticates an application's data-plane requests as a bearer token. It carries a grant for each Router it can use. Redis Iris issues agent keys through its Identity Service, not through the Intent Router API. -A grant lists one or more actions on one Router. A key without the action an operation needs gets `403`. +A grant lists one or more actions on one Router. The data plane refuses a request from a key without the action the operation needs. | Action | What it allows | | ------ | -------------- | diff --git a/content/develop/ai/context-engine/intent-router/developer-guide.md b/content/develop/ai/context-engine/intent-router/developer-guide.md index 9541309b40..99a9e23013 100644 --- a/content/develop/ai/context-engine/intent-router/developer-guide.md +++ b/content/develop/ai/context-engine/intent-router/developer-guide.md @@ -4,14 +4,14 @@ categories: - docs - develop - ai -description: Connect an application to Redis Intent Router and work with intents through Python, TypeScript, the Iris CLI, or HTTP. +description: Connect an application to Redis Intent Router and work with intents through Python, the Iris CLI, or HTTP. hideListLinks: true linktitle: Developer guide title: Redis Intent Router developer guide weight: 5 --- -Use the Python package, the TypeScript client, the Iris command-line interface (CLI), or Hypertext Transfer Protocol (HTTP) requests to author intents, match queries, and administer Routers. +Use the Python package, the Iris command-line interface (CLI), or Hypertext Transfer Protocol (HTTP) requests to author intents, match queries, and administer Routers. ## Integration workflow @@ -26,12 +26,11 @@ Platform teams create the Router and the agent keys. See [Manage Routers](/conte | Client | Use it when | Package and guide | |:-------|:------------|:------------------| -| Python | Your application or agent uses Python. | Install [`redis-iris`](https://pypi.org/project/redis-iris/) and follow the [quickstart](/content/develop/ai/context-engine/intent-router/quickstart.md). `IntentRouter` exposes the data-plane operations through its `client` property, and its `service` property administers Routers. | -| TypeScript | Your application or agent uses JavaScript or TypeScript. | Install [`@redis-iris/intent-router`](https://www.npmjs.com/package/@redis-iris/intent-router). `IntentRouter` calls the data plane, and `IntentRouterService` calls the control plane. | +| Python | Your application or agent uses Python. | Install [`redis-iris`](https://pypi.org/project/redis-iris/) and follow the [quickstart](/content/develop/ai/context-engine/intent-router/quickstart.md). `IntentRouter` matches queries and authors intents, and its `service` property administers Routers. Its `client` property is the generated client, for a call the library doesn't wrap. | | CLI | You work from a shell, a script, or continuous integration. | The `iris intent-router` commands ship in the same [`redis-iris`](https://pypi.org/project/redis-iris/) package. | | HTTP | You need language-independent access or don't want a package dependency. | No package required. Send requests as shown in the [API reference](/content/develop/ai/context-engine/intent-router/api-reference.md). | -The `redis-iris` package requires Python 3.11 or later. The TypeScript client requires Node.js 22 or later. The TypeScript client is a generated client, not a unified software development kit (SDK) equivalent to Python's `redis-iris` package. +The `redis-iris` package requires Python 3.11 or later. ## Connect to a Router @@ -41,7 +40,7 @@ Data-plane requests need: * The Router's ID, which starts with `rtr_`. * An agent key with a grant on the Router, sent as a bearer token on every request. Matching needs `read`, and authoring needs `write`. -Export them as `IRIS_INTENT_ROUTER_URL`, `IRIS_INTENT_ROUTER_ID`, and `IRIS_INTENT_ROUTER_API_KEY`. The CLI reads these variables, and in Python `IntentRouter.from_env()` builds a client from them. The TypeScript client doesn't read the environment, so the examples pass the same values to its constructor. +Export them as `IRIS_INTENT_ROUTER_URL`, `IRIS_INTENT_ROUTER_ID`, and `IRIS_INTENT_ROUTER_API_KEY`. The CLI reads these variables, and in Python `IntentRouter.from_env()` builds a client from them. Administration requests go to the control plane and need its URL and the admin token, which your platform team holds. The Python package and the CLI read them from `IRIS_INTENT_ROUTER_SERVICE_URL` and `IRIS_INTENT_ROUTER_SERVICE_TOKEN`. See [Manage Routers](/content/develop/ai/context-engine/intent-router/manage-routers.md#before-you-begin). @@ -49,20 +48,19 @@ Keep the admin token out of application configuration. An agent key can't create ## Work with intents -Create intents with `upsert`, add several at once with the bulk operation, and manage examples separately from an intent's configuration. See [Manage intents and examples](/content/develop/ai/context-engine/intent-router/manage-intents.md). +Create or replace an intent with `define` in Python or `intents put` in the CLI, add several at once with the bulk operation, and manage examples separately from an intent's configuration. See [Manage intents and examples](/content/develop/ai/context-engine/intent-router/manage-intents.md). ## Match queries -Send each query to `match` and read the best item's target. An empty list means no intent is close enough. See [Match queries](/content/develop/ai/context-engine/intent-router/match.md). +Send each query to `match` and read the best item's target. In Python, `match` returns the items best first, and `best` is the closest intent, or `None`. An empty list means no intent is close enough. See [Match queries](/content/develop/ai/context-engine/intent-router/match.md). ## Handle errors -Every client raises an error that carries the HTTP status and the problem `type`. Match on the `type`. A `409` from a Router that isn't serving, a `424` from the embedding provider, or a `503` is worth retrying with backoff. A `412` means the intent changed since you read it. A `401` means the key is invalid, and a `403` means it lacks the action the operation needs. See [service limits and errors](/content/develop/ai/context-engine/intent-router/limits.md#handle-request-errors). +A failed request returns an HTTP status and a problem `type`. Match on the `type`. A `409` from a Router that isn't serving, a `424` from the embedding provider, or a `503` is worth retrying with backoff. A `412` means the intent changed since you read it. A `401` means the key is missing, invalid, expired, or revoked. See [service limits and errors](/content/develop/ai/context-engine/intent-router/limits.md#handle-request-errors). ## References * [Quickstart](/content/develop/ai/context-engine/intent-router/quickstart.md) * [Python and CLI package reference](https://pypi.org/project/redis-iris/) -* [TypeScript client reference](https://www.npmjs.com/package/@redis-iris/intent-router) * [Data-plane API reference](/content/develop/ai/context-engine/intent-router/api-reference.md) * [Control-plane API reference](/content/operate/iris/intent-router/self-managed/control-plane-api-reference.md) diff --git a/content/develop/ai/context-engine/intent-router/limits.md b/content/develop/ai/context-engine/intent-router/limits.md index 4be8f4c126..63b40da122 100644 --- a/content/develop/ai/context-engine/intent-router/limits.md +++ b/content/develop/ai/context-engine/intent-router/limits.md @@ -59,17 +59,14 @@ The API reports failures as [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) p | Status | Type | Meaning and recovery | |:-------|:-----|:---------------------| -| `400` | `/errors/invalid-data` | A field is invalid, `limit` exceeds the cap, or `If-Match` and `expectedRevision` disagree. The `fields` list names each field and the rule it broke. Fix the request. | -| `400` | `/errors/invalid-cursor` | The `cursor` can't be read. Start again without a cursor. | +| `400` | `/errors/invalid-data` | A field is invalid, `limit` exceeds the cap, the `cursor` can't be read, or `If-Match` and `expectedRevision` disagree. The `fields` list names each field and the rule it broke. Fix the request, or start a list again without a cursor. | | `401` | `/errors/authentication-failed` | The agent key is missing, invalid, expired, or revoked. | -| `403` | `/errors/insufficient-permissions` | The key has no grant on this Router, or lacks the action the operation needs, such as `write` for authoring. | | `404` | `/errors/resource-not-found` | The Router, intent, or example doesn't exist, or the Router is being deleted. Deleting a missing intent succeeds instead, and so does deleting an example the intent doesn't hold. | | `408` | `/errors/timeout` | The request timed out. Retry with backoff. | -| `409` | `/errors/router-not-serving` | The Router is provisioning, has failed, or the data plane hasn't applied its latest configuration yet. Retry after a short wait, and check the Router's state if it persists. | -| `409` | `/errors/intent-limit-exceeded` | The Router already holds the maximum number of intents. Delete an intent or ask your administrator to raise the limit. | +| `409` | `/errors/conflict` | The Router is provisioning, has failed, or the data plane hasn't applied its latest configuration yet: retry after a short wait, and check the Router's state if it persists. Or the Router already holds the maximum number of intents: delete an intent or ask your administrator to raise the limit. | | `412` | `/errors/revision-conflict` | The intent changed since the revision you sent. Read the intent, reapply your change, and retry. | -| `423` | `/errors/router-suspended` | An administrator suspended the Router. Retrying doesn't help until it resumes. | -| `424` | `/errors/dependency-unavailable` | The embedding provider is unavailable. Retry with backoff. | +| `423` | `/errors/resource-suspended` | An administrator suspended the Router. Retrying doesn't help until it resumes. | +| `424` | `/errors/resource-unavailable` | The embedding provider is unavailable. Retry with backoff. | | `424` | `/errors/database-out-of-memory` | The Router's database is out of memory. Ask your administrator to add memory. | | `500` | `/errors/unexpected-error` | An unexpected internal error. Retry once, then contact support with the request details. | | `503` | `/errors/service-unavailable` | The Router's search index is rebuilding, or its database isn't answering. Wait for the `Retry-After` interval, then retry. | @@ -81,10 +78,11 @@ The API reports failures as [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) p | `400` | `/errors/invalid-data` | A field is invalid, the `vectorSpace` or `databaseId` isn't one the deployment offers, the deployment requires a `databaseId`, the `Idempotency-Key` is malformed, the requested state isn't `READY` or `SUSPENDED`, or an update is empty. | | `401` | `/errors/authentication-failed` | The admin token is missing or wrong. Agent keys are refused here. | | `404` | `/errors/resource-not-found` | The Router doesn't exist. Deleting a Router that is already gone succeeds instead. | +| `408` | `/errors/timeout` | The request timed out. Retry with backoff. | | `409` | `/errors/conflict` | The tenant has reached its Router limit, the Router isn't `READY` or `SUSPENDED` for an update, or a request with the same `Idempotency-Key` is still running. | | `412` | `/errors/revision-conflict` | The Router changed since the revision you sent. Read it and retry. | -| `422` | `/errors/idempotency-key-reused` | The `Idempotency-Key` was already used with a different request body. Use a new key. | -| `503` | `/errors/service-unavailable` | The control plane's storage isn't answering. Retry with backoff. | +| `424` | `/errors/resource-unavailable` | A store the control plane depends on is unavailable or unhealthy. Retry with backoff. | +| `500` | `/errors/unexpected-error` | An unexpected internal error. Retry once, then contact support with the request details. | ## Retry uncertain operations diff --git a/content/develop/ai/context-engine/intent-router/manage-intents.md b/content/develop/ai/context-engine/intent-router/manage-intents.md index 810645a8be..50e22ae6c8 100644 --- a/content/develop/ai/context-engine/intent-router/manage-intents.md +++ b/content/develop/ai/context-engine/intent-router/manage-intents.md @@ -17,7 +17,7 @@ Author the intents a Router matches against. The examples use the connection val Writing an intent with `PUT` creates it, or replaces the one with that name. It is idempotent: sending the same request twice produces the same intent, although each write returns a new `revision`. It returns `201` for a new intent and `200` for a replacement, and a replaced intent keeps its place in the list. -{{< clients-example set="iris_intent_router_intents" step="intent-upsert" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="intent-upsert" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} An intent takes up to 256 examples, each up to 2,048 characters. Phrasings already on the intent that your request omits are removed after the new ones are written, so a concurrent match never sees an empty intent. @@ -25,15 +25,15 @@ An intent takes up to 256 examples, each up to 2,048 characters. Phrasings alrea Add up to 100 intents in one request. They are added in the order you send them, after the intents the Router already has. An intent whose name already exists is replaced in place, and every other intent is left alone. Nothing is written if any intent is invalid. -{{< clients-example set="iris_intent_router_intents" step="intents-bulk" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="intents-bulk" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} ## Read and list intents Reading an intent returns its configuration, `exampleCount`, timestamps, and `revision`. It doesn't include the examples, which you list separately. -Listing returns intents with their configuration and example counts, in creation order. Pass `limit` (default 50, maximum 200). When more intents follow, the response includes `nextCursor`: pass it back as `cursor` for the next page. +Listing returns intents with their configuration and example counts, in creation order. Pass `limit` (default 50, maximum 200). When more intents follow, the response includes `nextCursor`: pass it back as `cursor` for the next page. In Python, `intents()` and `examples()` follow every page for you, and the CLI does with `--all`. -{{< clients-example set="iris_intent_router_intents" step="intents-list" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="intents-list" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} ## Update an intent's configuration @@ -44,15 +44,15 @@ Update the description, target, `minScore`, or metadata as a merge patch, withou - A `metadata` map replaces the whole existing map. - An empty body is rejected. -{{< clients-example set="iris_intent_router_intents" step="intent-patch" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="intent-patch" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} -Clearing `minScore` makes the intent use the Router's threshold again. +Clearing `minScore` makes the intent use the Router's threshold again. In Python, pass `None` to clear a field. The CLI's `intents patch` sets fields but can't clear one: to clear a field from the CLI, replace the intent with `intents put`. ## Avoid overwriting concurrent changes Every intent has a `revision` that changes with each write to the intent. Read the intent, then send its revision as `expectedRevision` with your change, so the write succeeds only if nobody changed the intent in between. This example changes the description and leaves the rest of the intent alone. A conditional `PUT` works the same way but replaces the whole intent. -{{< clients-example set="iris_intent_router_intents" step="intent-conditional" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="intent-conditional" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} A stale revision fails with `412` and the type `/errors/revision-conflict`. Read the intent again, reapply your change, and retry. Because every write returns a new revision, a retry of a conditional write that already succeeded also fails with `412`: read the intent to check. Adding or deleting examples through the example operations doesn't change the revision. @@ -60,27 +60,27 @@ A stale revision fails with `412` and the type `/errors/revision-conflict`. Read Add phrasings to an existing intent. Each request takes 1 to 256 examples and adds them after the existing ones. Repeating a phrasing keeps its place and doesn't embed it again. -{{< clients-example set="iris_intent_router_intents" step="examples-add" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="examples-add" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} The response lists each example with its `exampleId`. List an intent's examples in the order they were written, with the same `limit` and `cursor` pagination as intents. -{{< clients-example set="iris_intent_router_intents" step="examples-list" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="examples-list" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} -Read or delete one example by its identifier. Reading an identifier from another intent returns `404`. Deleting an example the intent doesn't hold succeeds and changes nothing, and deleting under an intent that doesn't exist returns `404`. +Read or delete one example by its identifier. Reading an identifier from another intent returns `404`. Deleting an example the intent doesn't hold succeeds and changes nothing, and deleting under an intent that doesn't exist returns `404`. The CLI deletes an example but has no command to read one. -{{< clients-example set="iris_intent_router_intents" step="example-delete" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="example-delete" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} ## Delete an intent Deleting an intent removes it and all its examples. Deleting a missing intent succeeds. -{{< clients-example set="iris_intent_router_intents" step="intent-delete" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="intent-delete" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} ## Clear a Router Clearing removes every intent and example and keeps the Router. The `scope` is required, and `INTENTS` is the only value. There is no default, so a request can't empty a Router by accident. Clearing also removes the quickstart's intents, which the [Match queries](/content/develop/ai/context-engine/intent-router/match.md) examples create again before they run. -{{< clients-example set="iris_intent_router_intents" step="router-clear" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_intents" step="router-clear" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} ## Write good examples diff --git a/content/develop/ai/context-engine/intent-router/manage-routers.md b/content/develop/ai/context-engine/intent-router/manage-routers.md index b3e8aefdc5..f204a05ed5 100644 --- a/content/develop/ai/context-engine/intent-router/manage-routers.md +++ b/content/develop/ai/context-engine/intent-router/manage-routers.md @@ -24,13 +24,21 @@ export IRIS_INTENT_ROUTER_SERVICE_URL='' export IRIS_INTENT_ROUTER_SERVICE_TOKEN='' ``` -The examples use Python (`IntentRouter.from_env().service`), TypeScript (`IntentRouterService`), cURL, and the `iris intent-router service` commands, which all read these variables. Follow one tab throughout the page. To choose a client, see the [developer guide](/content/develop/ai/context-engine/intent-router/developer-guide.md#choose-a-client). +The CLI's `service create` also mints the new Router's agent key, so it needs the data-plane URL and the Identity Service values too: + +```bash +export IRIS_INTENT_ROUTER_URL='' +export IRIS_IDENTITY_URL='' +export IRIS_IDENTITY_TOKEN='' +``` + +The examples use Python (`IntentRouter.from_env().service`), cURL, and the `iris intent-router service` commands, which all read these variables. Follow one tab throughout the page. To choose a client, see the [developer guide](/content/develop/ai/context-engine/intent-router/developer-guide.md#choose-a-client). ## Choose an embedding model A Router's embedding model decides how well matching understands wording, and it can't change after you create the Router. List the models your deployment offers. Embedding models have the `purpose` `EMBEDDING`, and you can filter the list with `purpose=EMBEDDING`. -{{< clients-example set="iris_intent_router_service" step="models-list" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_service" step="models-list" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} Each model reports its `provider`, `model`, and `dimensions`. Pass the same three values as the Router's `vectorSpace`. A self-managed deployment offers one embedding model, configured by your administrator, and holds its provider credentials itself, so `authorization` is `NONE`. Creating a Router with a model that isn't listed fails with `400`. @@ -40,11 +48,13 @@ The model also sets the Router's memory use. The Router's vector index takes abo Create a Router with a name and a `vectorSpace`. Optional fields set a description, a `template`, and a default `minScore`. -{{< clients-example set="iris_intent_router_service" step="router-create" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_service" step="router-create" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} Save the `routerId`. The new Router starts in `PROVISIONING` while the data plane builds its search index. The response carries a `Retry-After` header that suggests when to check again. -A tenant can hold up to 128 Routers by default. Router names don't have to be unique: the `routerId` identifies a Router. +The CLI's `service create` also mints an agent key with a `full` grant on the Router (`--role` picks another) and writes `IRIS_INTENT_ROUTER_URL`, `IRIS_INTENT_ROUTER_ID`, and `IRIS_INTENT_ROUTER_API_KEY` to the `--write-env` file with mode `0600`. The key is never printed. `--wait` waits for the Router to leave `PROVISIONING`. + +A tenant can hold up to 128 Routers by default. The API doesn't require unique Router names: the `routerId` identifies a Router. The CLI's `service create` refuses a name another Router already has, unless you pass `--reuse` to use that Router, so a rerun never creates a second Router. | Field | Meaning | | ----- | ------- | @@ -55,13 +65,13 @@ A tenant can hold up to 128 Routers by default. Router names don't have to be un | `minScore` | The Router's minimum score, above 0 and up to 1. Omit it, or send `0`, to use the default, 0.7. | | `databaseId` | The customer database that holds the Router's intents, examples, and index. Omit it to use the deployment's default database. Your administrator names the available databases, and a deployment can require one. It can't change after creation. | -To retry a create safely, send an `Idempotency-Key` header of the form `:`, with the body's object keys sorted and no extra whitespace. A retry with the same key and body returns the first response with `Idempotent-Replayed: true`, for 24 hours. Reusing the key with a different body fails with `422`. Only a successful response is kept, so a retry after a failure runs the create again. A retry that arrives while the first request is still running gets `409`. +To retry a create safely, send an `Idempotency-Key` header of the form `:`, with the body's object keys sorted and no extra whitespace. A retry with the same key and body returns the first response with `Idempotent-Replayed: true`, for 24 hours. Don't reuse a key with a different body. Only a successful response is kept, so a retry after a failure runs the create again. A retry that arrives while the first request is still running gets `409`. ## Wait for the Router to be ready -Poll the Router until it leaves `PROVISIONING`. Data requests to a Router that is still `PROVISIONING`, or has `FAILED`, return `409`. +Poll the Router until it leaves `PROVISIONING`. Data requests to a Router that is still `PROVISIONING`, or has `FAILED`, return `409`. In the CLI, `service create --wait` already waited, and exits with code `1` if the Router ended `FAILED`, so the CLI tab only reads the state. -{{< clients-example set="iris_intent_router_service" step="router-wait" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_service" step="router-wait" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} A `FAILED` Router reports why in `failureReason`, for example a database without vector search. A Router that isn't ready within the deployment's provisioning deadline, 10 minutes by default, also fails. A failed Router can't be resumed: delete it and create it again after you fix the cause. @@ -69,21 +79,21 @@ A `FAILED` Router reports why in `failureReason`, for example a database without List the deployment's Routers, or read one by ID. Every read returns the current `revision`, which conditional updates need. Lists return `nextCursor` when more Routers follow. -{{< clients-example set="iris_intent_router_service" step="router-list" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_service" step="router-list" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} ## Update a Router -Change a Router's name, description, template, or default `minScore` as a merge patch. An omitted field is unchanged, a `null` description clears it, and a `null` `minScore` resets it to the default. The embedding model and `databaseId` can't change. Changes apply after the data plane has applied the Router's new configuration, usually within seconds. +Change a Router's name, description, template, or default `minScore` as a merge patch. An omitted field is unchanged, a `null` description clears it, and a `null` `minScore` resets it to the default. The embedding model and `databaseId` can't change. Changes apply after the data plane has applied the Router's new configuration, usually within seconds. The CLI's `service update` doesn't change `template`. Send `expectedRevision`, or the revision as a quoted `If-Match` header, to make the update conditional: it fails with `412` if the Router changed since you read it. -{{< clients-example set="iris_intent_router_service" step="router-update" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_service" step="router-update" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} ## Suspend and resume a Router Suspend a Router to stop it serving without deleting its intents. Data requests return `423` while it is suspended. Setting the state back to `READY` resumes it. -{{< clients-example set="iris_intent_router_service" step="router-suspend" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_service" step="router-suspend" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} `SUSPENDED` and `READY` are the only states you can request, and only a `READY` or `SUSPENDED` Router can change between these states. Any other change fails with `409`. The control plane sets the other states. @@ -91,13 +101,17 @@ Suspend a Router to stop it serving without deleting its intents. Data requests Deleting a Router permanently removes it, its intents, its examples, and its search index. You can delete a Router in any state. Deleting a Router doesn't revoke its agent keys, because the keys live in the Identity Service, not the control plane. A key whose grant names a deleted Router stays in the Identity Service until you revoke it. Revoke the Router's keys before you delete it, as shown in [Rotate or revoke agent keys](/content/operate/iris/intent-router/self-managed/api-examples.md#rotate-or-revoke-agent-keys). -{{< clients-example set="iris_intent_router_service" step="router-delete" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_service" step="router-delete" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} The delete returns `202` with the Router in `DELETING`, and the data plane finishes the removal. Repeating the delete returns `202` while the removal runs and `204` once the Router is gone, so you can repeat a delete that timed out. ## Issue agent keys -The control plane doesn't issue agent keys. Your platform team issues them with the Redis Iris Identity Service, with a grant on the Router. See [Create a Router and an agent key](/content/operate/iris/intent-router/self-managed/api-examples.md). +The control plane doesn't issue agent keys. Your platform team issues them with the Redis Iris Identity Service, with a grant on the Router. See [Create a Router and an agent key](/content/operate/iris/intent-router/self-managed/api-examples.md). With the CLI, `service key` mints another key for a Router and writes it to a file: + +```bash +iris intent-router service key "$ROUTER_ID" --role read --name classifier --write classifier.env +``` | Action | What it allows | | ------ | -------------- | diff --git a/content/develop/ai/context-engine/intent-router/match.md b/content/develop/ai/context-engine/intent-router/match.md index 87ec90ee11..b3c4ff56af 100644 --- a/content/develop/ai/context-engine/intent-router/match.md +++ b/content/develop/ai/context-engine/intent-router/match.md @@ -19,7 +19,7 @@ The examples use the connection values from the [quickstart](/content/develop/ai A match request takes a query and returns the closest intents, best first. -{{< clients-example set="iris_intent_router_match" step="match-options" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_match" step="match-options" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} | Field | Required | Meaning | | ----- | -------- | ------- | @@ -43,7 +43,7 @@ A query with no close intent returns `200` with an empty list. The Router returns the target and leaves the action to your application. A typical handler takes the top item, checks that one exists, and dispatches on `target.type`. -{{< clients-example set="iris_intent_router_match" step="match-dispatch" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_match" step="match-dispatch" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} Decide in advance what your application does when the list is empty, such as asking a clarifying question or using a general-purpose agent. When two items score close together, the query may be ambiguous: consider asking the user instead of picking the first. @@ -66,7 +66,7 @@ Higher scores are stricter. Raise a threshold to reduce wrong matches and lower | Every intent is too strict or too loose | Change the Router's `minScore`. | | Compare scores while tuning | Match with `minScore` set to `0` to see the closest intents regardless of threshold, then pick thresholds. | -{{< clients-example set="iris_intent_router_match" step="match-tune" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_match" step="match-tune" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} `limit` can't exceed the deployment cap, which defaults to 50. A request above the cap is rejected with `400` rather than clamped, so you never silently receive fewer ranked results than you asked for. @@ -78,7 +78,6 @@ Match is a data-plane request, so it fails when its Router can't serve: - `400`: the query is empty or too long, or `limit` exceeds the cap. - `401`: the key is invalid, expired, or revoked. -- `403`: the key has no `read` grant on the Router. - `409`: the Router is provisioning or not serving. Retry after a short wait. - `423`: an administrator suspended the Router. - `424`: the embedding provider is unavailable. Retry with backoff. diff --git a/content/develop/ai/context-engine/intent-router/quickstart.md b/content/develop/ai/context-engine/intent-router/quickstart.md index e947e5407d..61a8f3dc08 100644 --- a/content/develop/ai/context-engine/intent-router/quickstart.md +++ b/content/develop/ai/context-engine/intent-router/quickstart.md @@ -4,7 +4,7 @@ categories: - docs - develop - ai -description: Define two intents, match queries against them, and update an intent with Python, TypeScript, HTTP, or the Iris CLI. +description: Define two intents, match queries against them, and update an intent with Python, HTTP, or the Iris CLI. hideListLinks: true linktitle: Quickstart title: Redis Intent Router quickstart @@ -19,7 +19,7 @@ You need a Router in the `READY` state and an agent key with `write` access to i This walkthrough needs no agent framework or model-provider key. -Follow one tab throughout the page. The Python package and the command-line interface (CLI) require Python 3.11 or later. The TypeScript client requires Node.js 22 or later. The cURL and CLI examples use Bash and jq. To choose a client, see the [developer guide](/content/develop/ai/context-engine/intent-router/developer-guide.md#choose-a-client). +Follow one tab throughout the page. The Python package and the command-line interface (CLI) require Python 3.11 or later. The cURL and CLI examples use Bash, and the cURL examples also use jq. To choose a client, see the [developer guide](/content/develop/ai/context-engine/intent-router/developer-guide.md#choose-a-client). **Python and CLI** @@ -30,12 +30,6 @@ source .venv/bin/activate python -m pip install redis-iris ``` -**TypeScript** - -```bash -npm install @redis-iris/intent-router -``` - **cURL** ```bash @@ -55,13 +49,13 @@ export IRIS_INTENT_ROUTER_API_KEY='' Keep keys out of source control and model prompts. -The Python examples use `IntentRouter` from the unified `redis-iris` package. `IntentRouter.from_env()` reads these variables, and its `client` property exposes the generated data-plane client, which the examples call. The TypeScript examples pass the same values to the generated `@redis-iris/intent-router` client. The CLI tab runs `iris intent-router` commands, which read the same variables. CLI commands use `--json` when saving a response or processing it with jq. +The Python examples use `IntentRouter` from the unified `redis-iris` package. `IntentRouter.from_env()` reads these variables, and its methods, such as `define`, `match`, and `update`, call the Router. The CLI tab runs `iris intent-router` commands, which read the same variables. ## Connect to the Router -Expand an example to copy the complete program. Save it as `quickstart.py`, `quickstart.mts`, `quickstart.sh` (cURL), or `quickstart.cli.sh`, then run `python quickstart.py`, `npx tsx quickstart.mts`, `bash quickstart.sh`, or `bash quickstart.cli.sh`. +Expand an example to copy the complete program. Save it as `quickstart.py`, `quickstart.sh` (cURL), or `quickstart.cli.sh`, then run `python quickstart.py`, `bash quickstart.sh`, or `bash quickstart.cli.sh`. -{{< clients-example set="iris_intent_router_quickstart" step="router-connect" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_quickstart" step="router-connect" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} The health check reports `ok: true` when the Router can serve. Each entry in `checks` covers one part: the Router's state, the embedding provider, the intent store, and the search index. When `ok` is `false`, the failing check's `message` says why, for example that the Router is still provisioning. Wait and retry. @@ -69,7 +63,7 @@ The health check reports `ok: true` when the Router can serve. Each entry in `ch Writing an intent with `PUT` creates it or replaces the one with the same name, so you can rerun this step. This example defines `track_order` with a description, a target, and three examples, and `refund` with examples only. -{{< clients-example set="iris_intent_router_quickstart" step="intents-create" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_quickstart" step="intents-create" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} Each intent reports its `exampleCount` and `revision`. The first request returns `201` because the intent is new. Repeating it returns `200` with the same intent and a new `revision`. @@ -77,7 +71,7 @@ Each intent reports its `exampleCount` and `revision`. The first request returns Send three queries. The response lists the closest intents, best first. -{{< clients-example set="iris_intent_router_quickstart" step="match-query" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_quickstart" step="match-query" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} The first query matches `track_order` and returns the target you stored. Your application reads `target.type` and `target.ref` to decide what to call. The second matches `refund`. The third, nonsense text, returns an empty list because no intent is close enough. An empty list is a successful `200` response. @@ -87,7 +81,7 @@ Scores vary with the Router's embedding model. If a query you expect to match re Add a phrasing to an existing intent. -{{< clients-example set="iris_intent_router_quickstart" step="examples-add" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_quickstart" step="examples-add" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} The next match uses the new example. Adding a phrasing that already exists is harmless. @@ -95,7 +89,7 @@ The next match uses the new example. Adding a phrasing that already exists is ha Updating an intent with `PATCH` changes its configuration without touching or re-embedding its examples. An omitted field is unchanged, and `null` clears a field. -{{< clients-example set="iris_intent_router_quickstart" step="intent-update" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_quickstart" step="intent-update" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} The intent now matches only queries that score at least `0.8`. Higher scores are stricter. @@ -103,7 +97,7 @@ The intent now matches only queries that score at least `0.8`. Higher scores are Delete the intents when you finish. Deleting a missing intent succeeds. To remove every intent at once and keep the Router, [clear the Router](/content/develop/ai/context-engine/intent-router/manage-intents.md#clear-a-router). -{{< clients-example set="iris_intent_router_quickstart_delete" step="intents-delete" description="" lang_filter="Python (Iris),TypeScript (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} +{{< clients-example set="iris_intent_router_quickstart_delete" step="intents-delete" description="" lang_filter="Python (Iris),cURL (Iris),CLI (Iris)" footer="hide" />}} ## Next steps diff --git a/content/operate/iris/intent-router/self-managed/api-examples.md b/content/operate/iris/intent-router/self-managed/api-examples.md index 948f7c7ecb..48f5cc3e17 100644 --- a/content/operate/iris/intent-router/self-managed/api-examples.md +++ b/content/operate/iris/intent-router/self-managed/api-examples.md @@ -31,7 +31,7 @@ umask 077 ## Create a Router -List the offered models, then create a Router with one of them and wait for it to be ready. See [Manage Routers](/content/develop/ai/context-engine/intent-router/manage-routers.md) for each step with Python, TypeScript, and the command-line interface (CLI). +List the offered models, then create a Router with one of them and wait for it to be ready. See [Manage Routers](/content/develop/ai/context-engine/intent-router/manage-routers.md) for each step with Python and the command-line interface (CLI). {{< clients-example set="iris_intent_router_service" step="models-list" description="" lang_filter="cURL (Iris),CLI (Iris)" footer="hide" />}} @@ -107,3 +107,27 @@ curl --fail-with-body --silent --show-error \ ``` A revoked key stops working within 300 seconds, the Identity Service's default cache lifetime. Revoke the keys for a Router before you delete it. + +## Use the CLI instead + +The `iris intent-router` CLI in the [redis-iris package](https://pypi.org/project/redis-iris/) runs the same steps. With the connection values and both tokens exported, list the offered models, then create a Router, wait for it to be ready, mint its agent key, and write the developer's environment file in one command: + +```bash +iris intent-router service models --purpose embedding +iris intent-router service create support-router --provider --model \ + --dimensions --role write --wait --write-env .env +``` + +`--write-env` writes `IRIS_INTENT_ROUTER_URL`, `IRIS_INTENT_ROUTER_ID`, and `IRIS_INTENT_ROUTER_API_KEY` to the file with mode `0600`, and the key is never printed. Check every hop, from the control plane to one data-plane read with the new key: + +```bash +iris intent-router service check --env-file .env +``` + +`service key` mints another key for the Router. `iris identity keys` lists, rotates, and revokes keys: + +```bash +iris identity keys list --product intent --resource-id +iris identity keys rotate --grace 10m --write .env --env IRIS_INTENT_ROUTER_API_KEY +iris identity keys revoke --yes +``` diff --git a/content/operate/iris/intent-router/self-managed/authentication.md b/content/operate/iris/intent-router/self-managed/authentication.md index 92d826b95a..f7298cd2da 100644 --- a/content/operate/iris/intent-router/self-managed/authentication.md +++ b/content/operate/iris/intent-router/self-managed/authentication.md @@ -27,7 +27,7 @@ This page lists each credential and what it unlocks. It also shows how to load t Every token defaults to `autoGenerate: true`: without `existingSecret`, the chart creates a random Secret named `-redis-intent-router--token`, with the key `token`, keeps it across upgrades, and never rotates it. For a release named `intent`, the admin token is in `intent-redis-intent-router-controlplane-admin-token`. Set `existingSecret` to supply your own, and set the matching `existingSecretChecksum` so the pods roll when the Secret changes. Two credentials can't reference the same Secret key, and rendering fails if they do. -An agent key gets `401` from the control plane. A key without a grant on a Router, or without the action an operation needs, gets `403` from the data plane. The data plane holds no credential for the control plane: the planes never call each other. +An agent key gets `401` from the control plane. The data plane refuses a key without a grant on the Router, or without the action an operation needs. The data plane holds no credential for the control plane: the planes never call each other. ## Load the administration tokens diff --git a/content/operate/iris/intent-router/self-managed/deploy.md b/content/operate/iris/intent-router/self-managed/deploy.md index 034c81b405..533bc106f3 100644 --- a/content/operate/iris/intent-router/self-managed/deploy.md +++ b/content/operate/iris/intent-router/self-managed/deploy.md @@ -146,7 +146,7 @@ curl -sS -X POST "$IRIS_INTENT_ROUTER_URL/iris/v2/intent/routers/$IRIS_INTENT_RO -d '{"query": "hello"}' ``` -A Router with no intents returns `200` and `{"items": []}`. A `401` means the key is wrong, and a `403` means it lacks a grant on the Router. A `409` means the Router isn't `READY` yet. +A Router with no intents returns `200` and `{"items": []}`. A `401` means the key is wrong. A `409` means the Router isn't `READY` yet. ## Next steps diff --git a/content/operate/iris/intent-router/self-managed/operations.md b/content/operate/iris/intent-router/self-managed/operations.md index 0258c42d3e..2a86145221 100644 --- a/content/operate/iris/intent-router/self-managed/operations.md +++ b/content/operate/iris/intent-router/self-managed/operations.md @@ -30,6 +30,8 @@ curl -sS "$IRIS_INTENT_ROUTER_URL/iris/v2/intent/routers/$IRIS_INTENT_ROUTER_ID/ -H "Authorization: Bearer $IRIS_INTENT_ROUTER_API_KEY" | jq . ``` +`iris intent-router health` runs the same check, and `iris intent-router service check ` checks every hop from the control plane to one data-plane read. + With `tests.enabled=true`, `helm test intent -n ` probes every service and runs the shared security-profile checks. ## Rotate credentials diff --git a/content/operate/iris/intent-router/self-managed/reference.md b/content/operate/iris/intent-router/self-managed/reference.md index 5895d079d3..0500a280d3 100644 --- a/content/operate/iris/intent-router/self-managed/reference.md +++ b/content/operate/iris/intent-router/self-managed/reference.md @@ -47,7 +47,7 @@ See [Configuration](/content/operate/iris/intent-router/self-managed/configurati | Data requests return `423` | The Router is suspended. | Set the Router's state to `READY`. | | Data requests return `503` with `Retry-After` | The Router's index is rebuilding, or its database is unreachable. | Wait for the interval, then check the database. | | Data requests return `401` | The key is wrong, expired, or revoked. | Mint or rotate the key. | -| Data requests return `403` | The key has no grant on the Router, or lacks the action, such as `write`. | Mint a key with the right grant. | +| A key works on one Router but is refused on another, or can match but not author | The key has no grant on that Router, or lacks the action, such as `write`. | Mint a key with the right grant. | | Creating a Router returns `409` | The tenant has reached `storage.maxRoutersPerTenant`. | Delete unused Routers or raise the limit. | | Updating a Router returns `409` | The Router isn't `READY` or `SUSPENDED`. | Wait for provisioning to finish, or delete a failed Router. | | Minting a key fails | `tenantId` differs from the Identity Service's tenant, or the Router doesn't exist. | Set both to the same tenant, and check the Router ID. | diff --git a/local_examples/iris/intent-router/intents/intents.cli.sh b/local_examples/iris/intent-router/intents/intents.cli.sh index 2a4585c4f0..e2f174e5a6 100644 --- a/local_examples/iris/intent-router/intents/intents.cli.sh +++ b/local_examples/iris/intent-router/intents/intents.cli.sh @@ -4,16 +4,11 @@ set -euo pipefail # HIDE_END # STEP_START intent-upsert -cat > refund.json <<'JSON' -{ - "description": "The customer wants their money back.", - "target": {"type": "agent", "ref": "billing-agent"}, - "minScore": 0.75, - "metadata": {"owner": "support"}, - "examples": ["i want a refund", "refund my purchase"] -} -JSON -iris intent-router intents upsert refund --file refund.json +iris intent-router intents put refund \ + --description "The customer wants their money back." \ + --target '{"type": "agent", "ref": "billing-agent"}' \ + --min-score 0.75 --metadata owner=support \ + --example "i want a refund" --example "refund my purchase" # STEP_END # STEP_START intents-bulk @@ -31,13 +26,13 @@ iris intent-router intents list --limit 50 # STEP_END # STEP_START intent-patch -iris intent-router intents update refund --clear-min-score \ - --target-type agent --target-ref refunds-agent +iris intent-router intents patch refund \ + --target '{"type": "agent", "ref": "refunds-agent"}' # STEP_END # STEP_START intent-conditional -REVISION=$(iris --json intent-router intents get refund | jq -er .revision) -iris intent-router intents update refund \ +REVISION=$(iris --json intent-router intents show refund | jq -er .revision) +iris intent-router intents patch refund \ --description "The customer wants a refund or a chargeback reversed." \ --expected-revision "$REVISION" # STEP_END @@ -53,7 +48,7 @@ jq -c '.items[] | {exampleId, text}' examples.json # STEP_START example-delete EXAMPLE_ID=$(jq -er '.items[0].exampleId' examples.json) -iris intent-router examples delete refund "$EXAMPLE_ID" +iris intent-router examples delete refund "$EXAMPLE_ID" --yes # STEP_END # STEP_START intent-delete @@ -61,5 +56,5 @@ iris intent-router intents delete cancel_order --yes # STEP_END # STEP_START router-clear -iris intent-router clear --scope INTENTS --yes +iris intent-router clear --scope intents --yes # STEP_END diff --git a/local_examples/iris/intent-router/intents/intents.mts b/local_examples/iris/intent-router/intents/intents.mts deleted file mode 100644 index 908458f70f..0000000000 --- a/local_examples/iris/intent-router/intents/intents.mts +++ /dev/null @@ -1,85 +0,0 @@ -// EXAMPLE: iris_intent_router_intents -// HIDE_START -import { IntentRouter } from "@redis-iris/intent-router"; - -function requiredEnv(name: string): string { - const value = process.env[name]; - if (!value) throw new Error(`Set ${name}`); - return value; -} - -const router = new IntentRouter({ - serverURL: requiredEnv("IRIS_INTENT_ROUTER_URL"), - routerId: requiredEnv("IRIS_INTENT_ROUTER_ID"), - apiKey: requiredEnv("IRIS_INTENT_ROUTER_API_KEY"), - retryConfig: { strategy: "none" }, -}); -// HIDE_END - -// STEP_START intent-upsert -const intent = await router.upsertIntent("refund", { - description: "The customer wants their money back.", - target: { type: "agent", ref: "billing-agent" }, - minScore: 0.75, - metadata: { owner: "support" }, - examples: ["i want a refund", "refund my purchase"], -}); -console.log(intent.name, intent.exampleCount, intent.revision); -// STEP_END - -// STEP_START intents-bulk -const result = await router.addIntents({ - intents: [ - { name: "track_order", examples: ["where is my order", "track my package"] }, - { name: "cancel_order", examples: ["cancel my order", "i changed my mind"] }, - ], -}); -for (const item of result.items) console.log(item.name, item.exampleCount); -// STEP_END - -// STEP_START intents-list -const page = await router.listIntents(50); -for (const item of page.items) console.log(item.name, item.exampleCount); -console.log("next cursor:", page.nextCursor); -// STEP_END - -// STEP_START intent-patch -const patched = await router.updateIntent("refund", { - minScore: null, - target: { type: "agent", ref: "refunds-agent" }, -}); -console.log(patched.name, patched.target, patched.minScore, patched.revision); -// STEP_END - -// STEP_START intent-conditional -const current = await router.getIntent("refund"); -const changed = await router.updateIntent("refund", { - description: "The customer wants a refund or a chargeback reversed.", - expectedRevision: current.revision, -}); -console.log(changed.name, changed.revision); -// STEP_END - -// STEP_START examples-add -const added = await router.addExamples("refund", { - examples: ["send my money back", "i was charged twice"], -}); -for (const example of added.items) console.log(example.exampleId, example.text); -// STEP_END - -// STEP_START examples-list -const examples = await router.listExamples("refund", 50); -for (const example of examples.items) console.log(example.exampleId, example.text); -// STEP_END - -// STEP_START example-delete -await router.deleteExample("refund", examples.items[0].exampleId); -// STEP_END - -// STEP_START intent-delete -await router.deleteIntent("cancel_order"); -// STEP_END - -// STEP_START router-clear -await router.clear({ scope: "INTENTS" }); -// STEP_END diff --git a/local_examples/iris/intent-router/intents/intents.py b/local_examples/iris/intent-router/intents/intents.py index 2e4b782d05..3217bcd494 100644 --- a/local_examples/iris/intent-router/intents/intents.py +++ b/local_examples/iris/intent-router/intents/intents.py @@ -1,15 +1,16 @@ # EXAMPLE: iris_intent_router_intents # HIDE_START -from redis_iris.intent_router import IntentRouter +from redis_iris import IntentRouter +from redis_iris.intent_router import Target router = IntentRouter.from_env() # HIDE_END # STEP_START intent-upsert -intent = router.client.upsert_intent( - intent_name="refund", +intent = router.define( + "refund", description="The customer wants their money back.", - target={"type": "agent", "ref": "billing-agent"}, + target=Target("agent", "billing-agent"), min_score=0.75, metadata={"owner": "support"}, examples=["i want a refund", "refund my purchase"], @@ -18,36 +19,32 @@ # STEP_END # STEP_START intents-bulk -result = router.client.add_intents( - intents=[ +added = router.define_many( + [ {"name": "track_order", "examples": ["where is my order", "track my package"]}, {"name": "cancel_order", "examples": ["cancel my order", "i changed my mind"]}, ] ) -for item in result.items: +for item in added: print(item.name, item.example_count) # STEP_END # STEP_START intents-list -page = router.client.list_intents(limit=50) -for item in page.items: +for item in router.intents(): print(item.name, item.example_count) -print("next cursor:", page.next_cursor) # STEP_END # STEP_START intent-patch -patched = router.client.update_intent( - intent_name="refund", - min_score=None, - target={"type": "agent", "ref": "refunds-agent"}, +patched = router.update( + "refund", min_score=None, target=Target("agent", "refunds-agent") ) print(patched.name, patched.target, patched.min_score, patched.revision) # STEP_END # STEP_START intent-conditional -current = router.client.get_intent(intent_name="refund") -changed = router.client.update_intent( - intent_name="refund", +current = router.intent("refund") +changed = router.update( + "refund", description="The customer wants a refund or a chargeback reversed.", expected_revision=current.revision, ) @@ -55,29 +52,26 @@ # STEP_END # STEP_START examples-add -added = router.client.add_examples( - intent_name="refund", examples=["send my money back", "i was charged twice"] -) -for example in added.items: +for example in router.add_examples("refund", ["send my money back", "i was charged twice"]): print(example.example_id, example.text) # STEP_END # STEP_START examples-list -examples = router.client.list_examples(intent_name="refund", limit=50) -for example in examples.items: +examples = router.examples("refund") +for example in examples: print(example.example_id, example.text) # STEP_END # STEP_START example-delete -router.client.delete_example( - intent_name="refund", example_id=examples.items[0].example_id -) +one = router.client.get_example(intent_name="refund", example_id=examples[0].example_id) +print(one.example_id, one.text) +router.remove_example("refund", one.example_id) # STEP_END # STEP_START intent-delete -router.client.delete_intent(intent_name="cancel_order") +router.remove("cancel_order") # STEP_END # STEP_START router-clear -router.client.clear(scope="INTENTS") +router.clear() # STEP_END diff --git a/local_examples/iris/intent-router/match/match.cli.sh b/local_examples/iris/intent-router/match/match.cli.sh index 4447ee46ed..1664388db9 100644 --- a/local_examples/iris/intent-router/match/match.cli.sh +++ b/local_examples/iris/intent-router/match/match.cli.sh @@ -1,18 +1,14 @@ # EXAMPLE: iris_intent_router_match # HIDE_START set -euo pipefail -cat > track_order.json <<'JSON' -{ - "description": "The customer wants to know where an order is.", - "target": {"type": "tool", "ref": "orders.track", "args": {"region": "eu"}}, - "examples": ["where is my order", "track my package", "has my parcel shipped"] -} -JSON -cat > refund.json <<'JSON' -{"examples": ["i want a refund", "refund my purchase", "i would like my money back"]} -JSON -iris intent-router intents upsert track_order --file track_order.json > /dev/null -iris intent-router intents upsert refund --file refund.json > /dev/null +iris intent-router intents put track_order \ + --description "The customer wants to know where an order is." \ + --target '{"type": "tool", "ref": "orders.track", "args": {"region": "eu"}}' \ + --example "where is my order" --example "track my package" \ + --example "has my parcel shipped" > /dev/null +iris intent-router intents put refund \ + --example "i want a refund" --example "refund my purchase" \ + --example "i would like my money back" > /dev/null # HIDE_END # STEP_START match-options diff --git a/local_examples/iris/intent-router/match/match.mts b/local_examples/iris/intent-router/match/match.mts deleted file mode 100644 index 4ffde7077d..0000000000 --- a/local_examples/iris/intent-router/match/match.mts +++ /dev/null @@ -1,51 +0,0 @@ -// EXAMPLE: iris_intent_router_match -// HIDE_START -import { IntentRouter } from "@redis-iris/intent-router"; - -function requiredEnv(name: string): string { - const value = process.env[name]; - if (!value) throw new Error(`Set ${name}`); - return value; -} - -const router = new IntentRouter({ - serverURL: requiredEnv("IRIS_INTENT_ROUTER_URL"), - routerId: requiredEnv("IRIS_INTENT_ROUTER_ID"), - apiKey: requiredEnv("IRIS_INTENT_ROUTER_API_KEY"), - retryConfig: { strategy: "none" }, -}); -await router.upsertIntent("track_order", { - description: "The customer wants to know where an order is.", - target: { type: "tool", ref: "orders.track", args: { region: "eu" } }, - examples: ["where is my order", "track my package", "has my parcel shipped"], -}); -await router.upsertIntent("refund", { - examples: ["i want a refund", "refund my purchase", "i would like my money back"], -}); -// HIDE_END - -// STEP_START match-options -const result = await router.match({ - query: "where is my order please", - limit: 3, - minScore: 0.6, -}); -for (const item of result.items) console.log(item.name, item.score, item.target); -// STEP_END - -// STEP_START match-dispatch -async function route(query: string): Promise { - const { items } = await router.match({ query }); - if (items.length === 0) return "no intent is close enough: use a default path"; - const top = items[0]; - if (!top.target) return `none ${top.name}`; - return `${top.target.type} ${top.target.ref}`; -} - -console.log(await route("where is my order please")); -// STEP_END - -// STEP_START match-tune -const candidates = await router.match({ query: "i need help", minScore: 0 }); -for (const item of candidates.items) console.log(item.name, item.score.toFixed(2)); -// STEP_END diff --git a/local_examples/iris/intent-router/match/match.py b/local_examples/iris/intent-router/match/match.py index 68ebb71539..e2a8694cca 100644 --- a/local_examples/iris/intent-router/match/match.py +++ b/local_examples/iris/intent-router/match/match.py @@ -1,41 +1,40 @@ # EXAMPLE: iris_intent_router_match # HIDE_START -from redis_iris.intent_router import IntentRouter +from redis_iris import IntentRouter +from redis_iris.intent_router import Target router = IntentRouter.from_env() -router.client.upsert_intent( - intent_name="track_order", +router.define( + "track_order", description="The customer wants to know where an order is.", - target={"type": "tool", "ref": "orders.track", "args": {"region": "eu"}}, + target=Target("tool", "orders.track", {"region": "eu"}), examples=["where is my order", "track my package", "has my parcel shipped"], ) -router.client.upsert_intent( - intent_name="refund", +router.define( + "refund", examples=["i want a refund", "refund my purchase", "i would like my money back"], ) # HIDE_END # STEP_START match-options -result = router.client.match(query="where is my order please", limit=3, min_score=0.6) -for item in result.items: +for item in router.match("where is my order please", limit=3, min_score=0.6): print(item.name, item.score, item.target) # STEP_END # STEP_START match-dispatch def route(query: str) -> str: - items = router.client.match(query=query).items - if not items: + best = router.match(query).best + if best is None: return "no intent is close enough: use a default path" - top = items[0] - if top.target is None: - return f"none {top.name}" - return f"{top.target.type} {top.target.ref}" + if best.target is None: + return f"none {best.name}" + return f"{best.target.type} {best.target.ref}" print(route("where is my order please")) # STEP_END # STEP_START match-tune -for item in router.client.match(query="i need help", min_score=0).items: +for item in router.match("i need help", min_score=0): print(item.name, round(item.score, 2)) # STEP_END diff --git a/local_examples/iris/intent-router/quickstart/quickstart.cli.sh b/local_examples/iris/intent-router/quickstart/quickstart.cli.sh index 90c7fe35c4..05e3c6c890 100644 --- a/local_examples/iris/intent-router/quickstart/quickstart.cli.sh +++ b/local_examples/iris/intent-router/quickstart/quickstart.cli.sh @@ -5,18 +5,14 @@ iris intent-router health # STEP_END # STEP_START intents-create -cat > track_order.json <<'JSON' -{ - "description": "The customer wants to know where an order is.", - "target": {"type": "tool", "ref": "orders.track", "args": {"region": "eu"}}, - "examples": ["where is my order", "track my package", "has my parcel shipped"] -} -JSON -cat > refund.json <<'JSON' -{"examples": ["i want a refund", "refund my purchase", "i would like my money back"]} -JSON -iris intent-router intents upsert track_order --file track_order.json -iris intent-router intents upsert refund --file refund.json +iris intent-router intents put track_order \ + --description "The customer wants to know where an order is." \ + --target '{"type": "tool", "ref": "orders.track", "args": {"region": "eu"}}' \ + --example "where is my order" --example "track my package" \ + --example "has my parcel shipped" +iris intent-router intents put refund \ + --example "i want a refund" --example "refund my purchase" \ + --example "i would like my money back" # STEP_END # STEP_START match-query @@ -30,5 +26,5 @@ iris intent-router examples add track_order "when will my delivery arrive" # STEP_END # STEP_START intent-update -iris intent-router intents update track_order --min-score 0.8 +iris intent-router intents patch track_order --min-score 0.8 # STEP_END diff --git a/local_examples/iris/intent-router/quickstart/quickstart.mts b/local_examples/iris/intent-router/quickstart/quickstart.mts deleted file mode 100644 index bc6a80d764..0000000000 --- a/local_examples/iris/intent-router/quickstart/quickstart.mts +++ /dev/null @@ -1,56 +0,0 @@ -// EXAMPLE: iris_intent_router_quickstart -// STEP_START router-connect -import { IntentRouter } from "@redis-iris/intent-router"; - -function requiredEnv(name: string): string { - const value = process.env[name]; - if (!value) throw new Error(`Set ${name}`); - return value; -} - -const router = new IntentRouter({ - serverURL: requiredEnv("IRIS_INTENT_ROUTER_URL"), - routerId: requiredEnv("IRIS_INTENT_ROUTER_ID"), - apiKey: requiredEnv("IRIS_INTENT_ROUTER_API_KEY"), - retryConfig: { strategy: "none" }, -}); -console.log(await router.getRouterHealth()); -// STEP_END - -// STEP_START intents-create -for (const intent of [ - await router.upsertIntent("track_order", { - description: "The customer wants to know where an order is.", - target: { type: "tool", ref: "orders.track", args: { region: "eu" } }, - examples: ["where is my order", "track my package", "has my parcel shipped"], - }), - await router.upsertIntent("refund", { - examples: ["i want a refund", "refund my purchase", "i would like my money back"], - }), -]) { - console.log(intent.name, intent.exampleCount, intent.revision); -} -// STEP_END - -// STEP_START match-query -for (const query of ["where is my order please", "i would like my money back", "zzz qqq"]) { - const { items } = await router.match({ query }); - if (items.length > 0) { - console.log(query, "->", items[0].name, items[0].score.toFixed(2), items[0].target); - } else { - console.log(query, "-> no match"); - } -} -// STEP_END - -// STEP_START examples-add -const added = await router.addExamples("track_order", { - examples: ["when will my delivery arrive"], -}); -for (const example of added.items) console.log(example.exampleId, example.text); -// STEP_END - -// STEP_START intent-update -const updated = await router.updateIntent("track_order", { minScore: 0.8 }); -console.log(updated.name, updated.minScore, updated.revision); -// STEP_END diff --git a/local_examples/iris/intent-router/quickstart/quickstart.py b/local_examples/iris/intent-router/quickstart/quickstart.py index 72ac02479a..aad3cdeb00 100644 --- a/local_examples/iris/intent-router/quickstart/quickstart.py +++ b/local_examples/iris/intent-router/quickstart/quickstart.py @@ -1,21 +1,23 @@ # EXAMPLE: iris_intent_router_quickstart # STEP_START router-connect -from redis_iris.intent_router import IntentRouter +from redis_iris import IntentRouter +from redis_iris.intent_router import Target router = IntentRouter.from_env() -print(router.client.get_router_health()) +health = router.health() +print(health.ok, [(check.name, check.ok, check.message) for check in health.checks]) # STEP_END # STEP_START intents-create for intent in ( - router.client.upsert_intent( - intent_name="track_order", + router.define( + "track_order", description="The customer wants to know where an order is.", - target={"type": "tool", "ref": "orders.track", "args": {"region": "eu"}}, + target=Target("tool", "orders.track", {"region": "eu"}), examples=["where is my order", "track my package", "has my parcel shipped"], ), - router.client.upsert_intent( - intent_name="refund", + router.define( + "refund", examples=["i want a refund", "refund my purchase", "i would like my money back"], ), ): @@ -24,22 +26,19 @@ # STEP_START match-query for query in ("where is my order please", "i would like my money back", "zzz qqq"): - matches = router.client.match(query=query).items - if matches: - print(query, "->", matches[0].name, round(matches[0].score, 2), matches[0].target) + best = router.match(query).best + if best: + print(query, "->", best.name, round(best.score, 2), best.target) else: print(query, "-> no match") # STEP_END # STEP_START examples-add -added = router.client.add_examples( - intent_name="track_order", examples=["when will my delivery arrive"] -) -for example in added.items: +for example in router.add_examples("track_order", ["when will my delivery arrive"]): print(example.example_id, example.text) # STEP_END # STEP_START intent-update -updated = router.client.update_intent(intent_name="track_order", min_score=0.8) +updated = router.update("track_order", min_score=0.8) print(updated.name, updated.min_score, updated.revision) # STEP_END diff --git a/local_examples/iris/intent-router/quickstart/quickstart_delete.cli.sh b/local_examples/iris/intent-router/quickstart/quickstart_delete.cli.sh index 9226cbe46e..3991f53cef 100644 --- a/local_examples/iris/intent-router/quickstart/quickstart_delete.cli.sh +++ b/local_examples/iris/intent-router/quickstart/quickstart_delete.cli.sh @@ -4,5 +4,6 @@ set -euo pipefail # HIDE_END # STEP_START intents-delete -iris intent-router intents delete track_order refund --yes +iris intent-router intents delete track_order --yes +iris intent-router intents delete refund --yes # STEP_END diff --git a/local_examples/iris/intent-router/quickstart/quickstart_delete.mts b/local_examples/iris/intent-router/quickstart/quickstart_delete.mts deleted file mode 100644 index 662de8d4e3..0000000000 --- a/local_examples/iris/intent-router/quickstart/quickstart_delete.mts +++ /dev/null @@ -1,23 +0,0 @@ -// EXAMPLE: iris_intent_router_quickstart_delete -// HIDE_START -import { IntentRouter } from "@redis-iris/intent-router"; - -function requiredEnv(name: string): string { - const value = process.env[name]; - if (!value) throw new Error(`Set ${name}`); - return value; -} - -const router = new IntentRouter({ - serverURL: requiredEnv("IRIS_INTENT_ROUTER_URL"), - routerId: requiredEnv("IRIS_INTENT_ROUTER_ID"), - apiKey: requiredEnv("IRIS_INTENT_ROUTER_API_KEY"), - retryConfig: { strategy: "none" }, -}); -// HIDE_END - -// STEP_START intents-delete -for (const name of ["track_order", "refund"]) { - await router.deleteIntent(name); -} -// STEP_END diff --git a/local_examples/iris/intent-router/quickstart/quickstart_delete.py b/local_examples/iris/intent-router/quickstart/quickstart_delete.py index 2959db7c13..511acd5bbd 100644 --- a/local_examples/iris/intent-router/quickstart/quickstart_delete.py +++ b/local_examples/iris/intent-router/quickstart/quickstart_delete.py @@ -1,11 +1,11 @@ # EXAMPLE: iris_intent_router_quickstart_delete # HIDE_START -from redis_iris.intent_router import IntentRouter +from redis_iris import IntentRouter router = IntentRouter.from_env() # HIDE_END # STEP_START intents-delete for name in ("track_order", "refund"): - router.client.delete_intent(intent_name=name) + router.remove(name) # STEP_END diff --git a/local_examples/iris/intent-router/service/service.cli.sh b/local_examples/iris/intent-router/service/service.cli.sh index 96608eabe4..f0fb61def3 100644 --- a/local_examples/iris/intent-router/service/service.cli.sh +++ b/local_examples/iris/intent-router/service/service.cli.sh @@ -4,40 +4,40 @@ set -euo pipefail # HIDE_END # STEP_START models-list -iris --json intent-router service models list --purpose EMBEDDING > models.json +iris --json intent-router service models --purpose embedding > models.json jq -c '.items[] | {provider, model, dimensions, authorization}' models.json # STEP_END # STEP_START router-create -iris --json intent-router service routers create support-router \ +ROUTER_ID=$(iris --json intent-router service create support-router \ --description "Routes support requests to tools and agents." \ - --template AGENT --min-score 0.7 \ + --template agent --min-score 0.7 \ --provider "$(jq -er '.items[0].provider' models.json)" \ --model "$(jq -er '.items[0].model' models.json)" \ - --dimensions "$(jq -er '.items[0].dimensions' models.json)" > router.json -ROUTER_ID=$(jq -er .routerId router.json) -jq -c '{routerId, state, revision}' router.json + --dimensions "$(jq -er '.items[0].dimensions' models.json)" \ + --wait --write-env .env | jq -er .routerId) # STEP_END # STEP_START router-wait -iris --json intent-router service routers wait "$ROUTER_ID" > router.json -jq -c '{routerId, state, failureReason}' router.json +iris --json intent-router service show "$ROUTER_ID" \ + | jq -c '{routerId, state, failureReason}' # STEP_END # STEP_START router-list -iris intent-router service routers list +iris intent-router service list # STEP_END # STEP_START router-update -iris intent-router service routers update "$ROUTER_ID" --min-score 0.75 \ - --expected-revision "$(jq -er .revision router.json)" +REVISION=$(iris --json intent-router service show "$ROUTER_ID" | jq -er .revision) +iris intent-router service update "$ROUTER_ID" --min-score 0.75 \ + --expected-revision "$REVISION" # STEP_END # STEP_START router-suspend -iris intent-router service routers suspend "$ROUTER_ID" -iris intent-router service routers resume "$ROUTER_ID" +iris intent-router service update "$ROUTER_ID" --state SUSPENDED +iris intent-router service update "$ROUTER_ID" --state READY # STEP_END # STEP_START router-delete -iris intent-router service routers delete "$ROUTER_ID" --yes +iris intent-router service delete "$ROUTER_ID" --yes # STEP_END diff --git a/local_examples/iris/intent-router/service/service.mts b/local_examples/iris/intent-router/service/service.mts deleted file mode 100644 index 606f8a1b88..0000000000 --- a/local_examples/iris/intent-router/service/service.mts +++ /dev/null @@ -1,74 +0,0 @@ -// EXAMPLE: iris_intent_router_service -// HIDE_START -import { IntentRouterService } from "@redis-iris/intent-router"; - -function requiredEnv(name: string): string { - const value = process.env[name]; - if (!value) throw new Error(`Set ${name}`); - return value; -} - -const service = new IntentRouterService({ - serverURL: requiredEnv("IRIS_INTENT_ROUTER_SERVICE_URL"), - token: requiredEnv("IRIS_INTENT_ROUTER_SERVICE_TOKEN"), - retryConfig: { strategy: "none" }, -}); -// HIDE_END - -// STEP_START models-list -const models = await service.listModels("EMBEDDING"); -for (const model of models.items) { - console.log(model.provider, model.model, model.dimensions, model.authorization); -} -// STEP_END - -// STEP_START router-create -const created = await service.createRouter({ - name: "support-router", - description: "Routes support requests to tools and agents.", - template: "AGENT", - minScore: 0.7, - vectorSpace: { - provider: models.items[0].provider, - model: models.items[0].model, - dimensions: models.items[0].dimensions, - }, -}); -const routerId = created.routerId; -console.log(routerId, created.state, created.revision); -// STEP_END - -// STEP_START router-wait -let router = await service.getRouter(routerId); -while (router.state === "PROVISIONING") { - await new Promise((resolve) => setTimeout(resolve, 1000)); - router = await service.getRouter(routerId); -} -console.log(router.routerId, router.state, router.failureReason); -if (router.state !== "READY") process.exit(1); -// STEP_END - -// STEP_START router-list -const routers = await service.listRouters(); -for (const item of routers.items) console.log(item.routerId, item.name, item.state); -// STEP_END - -// STEP_START router-update -const updated = await service.updateRouter(routerId, { - minScore: 0.75, - expectedRevision: router.revision, -}); -console.log(updated.routerId, updated.minScore, updated.revision); -// STEP_END - -// STEP_START router-suspend -for (const state of ["SUSPENDED", "READY"] as const) { - const changed = await service.updateRouter(routerId, { state }); - console.log(changed.routerId, changed.state); -} -// STEP_END - -// STEP_START router-delete -const deleted = await service.deleteRouter(routerId); -console.log(deleted?.routerId, deleted?.state); -// STEP_END diff --git a/local_examples/iris/intent-router/service/service.py b/local_examples/iris/intent-router/service/service.py index 4774ae439a..8d6aa3efb5 100644 --- a/local_examples/iris/intent-router/service/service.py +++ b/local_examples/iris/intent-router/service/service.py @@ -1,13 +1,14 @@ # EXAMPLE: iris_intent_router_service # HIDE_START -from redis_iris.intent_router import IntentRouter +from redis_iris import IntentRouter +from redis_iris.intent_router import models service = IntentRouter.from_env().service # HIDE_END # STEP_START models-list -models = service.list_models(purpose="EMBEDDING").items -for model in models: +offered = service.list_models(purpose=models.ModelPurpose.EMBEDDING).items +for model in offered: print(model.provider, model.model, model.dimensions, model.authorization) # STEP_END @@ -15,13 +16,13 @@ created = service.create_router( name="support-router", description="Routes support requests to tools and agents.", - template="AGENT", + template=models.RouterTemplate.AGENT, min_score=0.7, - vector_space={ - "provider": models[0].provider, - "model": models[0].model, - "dimensions": models[0].dimensions, - }, + vector_space=models.EmbeddingModel( + provider=offered[0].provider, + model=offered[0].model, + dimensions=offered[0].dimensions, + ), ) router_id = created.router_id print(router_id, created.state, created.revision) @@ -52,12 +53,13 @@ # STEP_END # STEP_START router-suspend -for state in ("SUSPENDED", "READY"): +for state in (models.MutableRouterState.SUSPENDED, models.MutableRouterState.READY): changed = service.update_router(router_id=router_id, state=state) print(changed.router_id, changed.state) # STEP_END # STEP_START router-delete deleted = service.delete_router(router_id=router_id) -print(deleted.router_id, deleted.state) +if deleted is not None: # None when the Router was already gone + print(deleted.router_id, deleted.state) # STEP_END