Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Create a playbook API key with `write` and `publish` permissions. Both include `

This walkthrough needs no agent framework or model-provider key.

Follow one tab throughout the page. The Python SDK and command-line interface (CLI) are prereleases. Both require Python 3.11 or later. The cURL and CLI examples use Bash and jq. To connect the CLI to your deployment with a profile, see [SDK and CLI setup](/content/develop/ai/context-engine/sdk-and-cli.md).
Follow one tab throughout the page. The Python SDK and command-line interface (CLI) require Python 3.11 or later. The cURL and CLI examples use Bash and jq. To connect the CLI to your deployment with a profile, see [SDK and CLI setup](/content/develop/ai/context-engine/sdk-and-cli.md).

**Python and CLI**

Expand Down
16 changes: 8 additions & 8 deletions content/develop/ai/context-engine/lens/read-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ weight: 35
Use the Lens read application programming interface (API) to list sessions and read turns from code, for example to export failed turns or build your own reports. The Python examples use the Lens read client:

```bash
python -m pip install 'redis-agent-lens>=0.1.0a4'
python -m pip install 'redis-agent-lens>=1.0.0'
```

The cURL examples use Bash and jq. Every operation is a `GET` request.
Expand Down Expand Up @@ -51,7 +51,7 @@ List one agent's sessions over a time range, newest activity first. This example
| `range` | Required. A lookback duration such as `24h`, `7d`, or `30d`. |
| `sessionIdPrefix` | Sessions whose ID starts with this value, ignoring case. At least 2 characters. |
| `userId` | Sessions with this user ID, ignoring case. |
| `outcome` | Sessions with a turn that has any of these outcomes. Use `unset` for turns without an outcome. |
| `outcome` | Sessions with a turn that has any of these outcomes. Use `UNSET` for turns without an outcome. |
| `limit` | Sessions per page, up to 200. Default 50. |
| `cursor` | The `nextCursor` value from the previous page. |

Expand Down Expand Up @@ -79,10 +79,10 @@ Each manifest entry has one of these states:

| State | Meaning |
| ----- | ------- |
| `entered-prompt` | At least one result entered the prompt. |
| `fetched-not-used` | The source returned results, and none entered the prompt. |
| `invoked-empty` | The source returned no results. |
| `not-instrumented` | The turn recorded no prompt assembly, or the results had no text to match. |
| `ENTERED_PROMPT` | At least one result entered the prompt. |
| `FETCHED_NOT_USED` | The source returned results, and none entered the prompt. |
| `INVOKED_EMPTY` | The source returned no results. |
| `NOT_INSTRUMENTED` | The turn recorded no prompt assembly, or the results had no text to match. |

Each candidate in an entry includes its ID, score where the source provides one, and whether it entered the prompt.

Expand All @@ -92,15 +92,15 @@ Captured text, such as a retrieved document or a model prompt, is stored by cont

{{< clients-example set="iris_lens_read_api" step="get_content" description="" lang_filter="Python (Iris),cURL (Iris)" footer="hide" />}}

The response's `availability` is `available` with the text in `body`, or says why the text isn't available, such as `expired` or `not-captured`. See [retention](/content/develop/ai/context-engine/lens/concepts.md#retention).
The response's `availability` is `AVAILABLE` with the text in `body`, or says why the text isn't available, such as `EXPIRED` or `NOT_CAPTURED`. See [retention](/content/develop/ai/context-engine/lens/concepts.md#retention).

## Get a Context Gateway tool result

Read the result of a Context Gateway tool call in a turn, by the step's ID. The turn lists these steps in `contextRetrieverToolSteps`:

{{< clients-example set="iris_lens_read_api" step="gateway_result" description="" lang_filter="Python (Iris),cURL (Iris)" footer="hide" />}}

The result classifies what the tool returned, such as `records` or `aggregate`, and includes the returned data.
The result classifies what the tool returned, such as `RECORDS` or `AGGREGATE`, and includes the returned data.

## Next steps

Expand Down
4 changes: 2 additions & 2 deletions local_examples/iris/lens/read_api/read_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
# STEP_END

# STEP_START list_sessions
page = client.list_sessions(agent_id="support-bot", range="24h", outcome=["failed"])
page = client.list_sessions(agent_id="support-bot", range="24h", outcome=["FAILED"])
print(f"Showing {page.effective_range} of sessions")

sessions = list(page.items)
while page.next_cursor:
page = client.list_sessions(
agent_id="support-bot", range="24h", outcome=["failed"], cursor=page.next_cursor
agent_id="support-bot", range="24h", outcome=["FAILED"], cursor=page.next_cursor
)
sessions.extend(page.items)

Expand Down
2 changes: 1 addition & 1 deletion local_examples/iris/lens/read_api/read_api.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ curl --fail-with-body --silent --show-error "$LENS_API/agents" -H "$AUTH" \
curl --fail-with-body --silent --show-error -G "$LENS_API/sessions" -H "$AUTH" \
--data-urlencode "agentId=support-bot" \
--data-urlencode "range=24h" \
--data-urlencode "outcome=failed" > sessions.json
--data-urlencode "outcome=FAILED" > sessions.json
jq '{effectiveRange, nextCursor, sessions: [.items[] | {sessionId, turnCount, lastSeen}]}' sessions.json
SESSION_ID=$(jq -er '.items[0].sessionId' sessions.json)
# STEP_END
Expand Down
2 changes: 1 addition & 1 deletion local_examples/iris/playbooks/async/async_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async def main():
query="Where can I see how my refund is progressing?",
limit=5,
)
for match in results.matches:
for match in results.items:
if match.faq is not None:
print(match.faq.answer)

Expand Down
11 changes: 5 additions & 6 deletions local_examples/iris/playbooks/faqs/faqs.mts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const results = await playbook.searchFaqs({
filter: { eq: { name: "locale", value: "en-US" } },
limit: 5,
});
for (const match of results.matches) {
for (const match of results.items) {
if (match.faq) {
console.log(match.entryId, match.version, match.score, match.faq.answer);
}
Expand All @@ -73,10 +73,9 @@ await playbook.publishFaq(faqId, { version });
let faqCursor: string | undefined;
while (true) {
const page = await playbook.listFaqs(
"updatedAt", "desc", undefined, undefined, undefined, 50, faqCursor,
"updatedAt", "DESC", undefined, undefined, undefined, 50, faqCursor,
);
if (faqCursor === undefined) console.log("Total FAQs:", page.total ?? "unknown");
for (const entry of page.faqs) console.log(entry.entryId, entry.latestVersion, entry.publication);
for (const entry of page.items) console.log(entry.entryId, entry.latestVersion, entry.publication);
faqCursor = page.nextCursor;
if (faqCursor === undefined) break;
}
Expand All @@ -86,7 +85,7 @@ while (true) {
let faqVersionCursor: string | undefined;
while (true) {
const page = await playbook.listFaqVersions(faqId, undefined, undefined, 50, faqVersionCursor);
for (const item of page.versions) console.log(item.version, item.contentDigest, item.content);
for (const item of page.items) console.log(item.version, item.contentDigest, item.content);
faqVersionCursor = page.nextCursor;
if (faqVersionCursor === undefined) break;
}
Expand All @@ -97,7 +96,7 @@ const lookup = await playbook.lookupEntries({
references: [{ entryId: faqId }, { entryKey: "refund-status" }],
});
console.log("Version limit:", lookup.maxVersions);
for (const result of lookup.results) {
for (const result of lookup.items) {
if ("found" in result) {
const state = result.found.entry;
console.log(state.entryId, state.versionCount, state.latest, state.publication);
Expand Down
12 changes: 5 additions & 7 deletions local_examples/iris/playbooks/faqs/faqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
filter_={"eq": {"name": "locale", "value": "en-US"}},
limit=5,
)
for match in results.matches:
for match in results.items:
if match.faq is not None:
print(match.entry_id, match.version, match.score, match.faq.answer)
# STEP_END
Expand All @@ -77,10 +77,8 @@
# STEP_START playbook-faq-list
cursor = None
while True:
page = playbook.client.list_faqs(sort_by="updatedAt", sort_order="desc", limit=50, cursor=cursor)
if cursor is None:
print("Total FAQs:", page.total if page.total is not None else "unknown")
for entry in page.faqs:
page = playbook.client.list_faqs(sort_by="updatedAt", sort_order="DESC", limit=50, cursor=cursor)
for entry in page.items:
print(entry.entry_id, entry.latest_version, entry.publication)
cursor = page.next_cursor
if cursor is None:
Expand All @@ -91,7 +89,7 @@
cursor = None
while True:
page = playbook.client.list_faq_versions(faq_id=faq_id, limit=50, cursor=cursor)
for item in page.versions:
for item in page.items:
print(item.version, item.content_digest, item.content)
cursor = page.next_cursor
if cursor is None:
Expand All @@ -108,7 +106,7 @@
]
)
print("Version limit:", lookup.max_versions)
for result in lookup.results:
for result in lookup.items:
if isinstance(result, Found):
state = result.found.entry
print(state.entry_id, state.version_count, state.latest, state.publication)
Expand Down
32 changes: 15 additions & 17 deletions local_examples/iris/playbooks/proposals/proposals.mts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ console.log(await playbook.getPlaybookHealth());
// STEP_START playbook-proposal-inbox
let inboxCursor: string | undefined;
while (true) {
const page = await playbook.listProposals("inbox", "updatedAt", "desc", 50, inboxCursor);
if (inboxCursor === undefined) console.log("Total inbox proposals:", page.total ?? "unknown");
for (const item of page.proposals) console.log(item.proposalId, item.status, item.evidenceCount, item.action);
const page = await playbook.listProposals("INBOX", "updatedAt", "DESC", 50, inboxCursor);
for (const item of page.items) console.log(item.proposalId, item.state, item.evidenceCount, item.action);
inboxCursor = page.nextCursor;
if (inboxCursor === undefined) break;
}
Expand All @@ -31,12 +30,12 @@ while (true) {
// STEP_START playbook-proposal-search
const related = await playbook.searchProposals({
query: "Contact support about a missing refund",
minimumScore: 0.7,
actionType: "add",
entryType: "faq",
minScore: 0.7,
actionType: "ADD",
entryKind: "FAQ",
limit: 5,
});
for (const match of related.matches) console.log(match.proposal.proposalId, match.score, match.proposal.action);
for (const match of related.items) console.log(match.proposal.proposalId, match.score, match.proposal.action);
// STEP_END

// STEP_START playbook-proposal-create
Expand All @@ -54,12 +53,12 @@ const action = { add: {
const proposal = await playbook.createProposal({
action,
source: {
type: "document",
kind: "document",
reference: { documentId: "sample-refund-policy" },
},
});
const proposalId = proposal.proposalId;
console.log(proposalId, proposal.proposalRevision);
console.log(proposalId, proposal.revision);
// STEP_END

// STEP_START playbook-proposal-evidence
Expand All @@ -70,7 +69,7 @@ const evidence = await playbook.submitProposalEvidence(proposalId, evidenceId, {
observedAt,
producer: { name: "docs-example", promptVersion: "refund-review-v1" },
source: {
type: "document",
kind: "document",
reference: { documentId: "sample-refund-policy" },
content: { excerpt: "For refund inquiries, collect the order number and request date. Do not request full card details." },
},
Expand Down Expand Up @@ -99,14 +98,14 @@ const revisedAction = { add: {
} },
} };
const revised = await playbook.replaceProposal(proposalId, {
action: revisedAction, expectedProposalRevision: reviewed.proposalRevision,
action: revisedAction, expectedRevision: reviewed.revision,
});
console.log(revised.proposalRevision, revised.action);
console.log(revised.revision, revised.action);
// STEP_END

// STEP_START playbook-proposal-trash
await playbook.dismissProposal(proposalId, {
expectedProposalRevision: revised.proposalRevision,
expectedRevision: revised.revision,
});
await playbook.restoreProposal(proposalId);
// To purge a trashed proposal instead of restoring it:
Expand All @@ -120,7 +119,7 @@ console.log(finalReview.action, finalReview.evidence);

// STEP_START playbook-proposal-accept
const accepted = await playbook.acceptProposal(proposalId, {
expectedProposalRevision: finalReview.proposalRevision,
expectedRevision: finalReview.revision,
});
if (!("versionCreated" in accepted.result)) {
throw new Error("Review the unrecognized acceptance result before publishing");
Expand All @@ -137,9 +136,8 @@ await playbook.publishFaq(receipt.entryId, { version: receipt.version });
// STEP_START playbook-proposal-history
let trashCursor: string | undefined;
while (true) {
const page = await playbook.listProposals("trash", "updatedAt", "desc", 50, trashCursor);
if (trashCursor === undefined) console.log("Total trash proposals:", page.total ?? "unknown");
for (const item of page.proposals) console.log(item.proposalId, item.status, item.evidenceCount, item.action);
const page = await playbook.listProposals("TRASH", "updatedAt", "DESC", 50, trashCursor);
for (const item of page.items) console.log(item.proposalId, item.state, item.evidenceCount, item.action);
trashCursor = page.nextCursor;
if (trashCursor === undefined) break;
}
Expand Down
38 changes: 17 additions & 21 deletions local_examples/iris/playbooks/proposals/proposals.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@
cursor = None
while True:
page = playbook.client.list_proposals(
view="inbox", sort_by="updatedAt", sort_order="desc", limit=50, cursor=cursor
view="INBOX", sort_by="updatedAt", sort_order="DESC", limit=50, cursor=cursor
)
if cursor is None:
print("Total inbox proposals:", page.total if page.total is not None else "unknown")
for item in page.proposals:
print(item.proposal_id, item.status, item.evidence_count, item.action)
for item in page.items:
print(item.proposal_id, item.state, item.evidence_count, item.action)
cursor = page.next_cursor
if cursor is None:
break
Expand All @@ -32,12 +30,12 @@
# STEP_START playbook-proposal-search
related = playbook.client.search_proposals(
query="Contact support about a missing refund",
minimum_score=0.7,
action_type="add",
entry_type="faq",
min_score=0.7,
action_type="ADD",
entry_kind="FAQ",
limit=5,
)
for match in related.matches:
for match in related.items:
print(match.proposal.proposal_id, match.score, match.proposal.action)
# STEP_END

Expand All @@ -62,12 +60,12 @@
proposal = playbook.client.create_proposal(
action=action,
source={
"type": "document",
"kind": "document",
"reference": {"documentId": "sample-refund-policy"},
},
)
proposal_id = proposal.proposal_id
print(proposal_id, proposal.proposal_revision)
print(proposal_id, proposal.revision)
# STEP_END

# STEP_START playbook-proposal-evidence
Expand All @@ -82,7 +80,7 @@
observed_at=observed_at,
producer={"name": "docs-example", "prompt_version": "refund-review-v1"},
source={
"type": "document",
"kind": "document",
"reference": {"documentId": "sample-refund-policy"},
"content": {
"excerpt": (
Expand All @@ -108,15 +106,15 @@
revised = playbook.client.replace_proposal(
proposal_id=proposal_id,
action=revised_action,
expected_proposal_revision=reviewed.proposal_revision,
expected_revision=reviewed.revision,
)
print(revised.proposal_revision, revised.action)
print(revised.revision, revised.action)
# STEP_END

# STEP_START playbook-proposal-trash
playbook.client.dismiss_proposal(
proposal_id=proposal_id,
expected_proposal_revision=revised.proposal_revision,
expected_revision=revised.revision,
)
playbook.client.restore_proposal(proposal_id=proposal_id)
# To purge a trashed proposal instead of restoring it:
Expand All @@ -133,7 +131,7 @@

accepted = playbook.client.accept_proposal(
proposal_id=proposal_id,
expected_proposal_revision=final_review.proposal_revision,
expected_revision=final_review.revision,
)
if not isinstance(accepted.result, models.VersionCreated):
raise ValueError("Review the unrecognized acceptance result before publishing")
Expand All @@ -153,12 +151,10 @@
cursor = None
while True:
page = playbook.client.list_proposals(
view="trash", sort_by="updatedAt", sort_order="desc", limit=50, cursor=cursor
view="TRASH", sort_by="updatedAt", sort_order="DESC", limit=50, cursor=cursor
)
if cursor is None:
print("Total trash proposals:", page.total if page.total is not None else "unknown")
for item in page.proposals:
print(item.proposal_id, item.status, item.evidence_count, item.action)
for item in page.items:
print(item.proposal_id, item.state, item.evidence_count, item.action)
cursor = page.next_cursor
if cursor is None:
break
Expand Down
4 changes: 2 additions & 2 deletions local_examples/iris/playbooks/quickstart/quickstart.mts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const results = await playbook.searchFaqs({
filter: { eq: { name: "locale", value: "en-US" } },
limit: 5,
});
for (const match of results.matches) {
for (const match of results.items) {
if (match.faq) {
console.log(match.entryId, match.version, match.score, match.faq.answer);
}
Expand Down Expand Up @@ -77,7 +77,7 @@ const updatedResults = await playbook.searchFaqs({
filter: { eq: { name: "locale", value: "en-US" } },
limit: 5,
});
for (const match of updatedResults.matches) {
for (const match of updatedResults.items) {
if (match.entryId === faqId && match.faq) {
console.log("Published answer:", match.version, match.faq.answer);
}
Expand Down
Loading
Loading