Skip to content

Commit 2d2ec9d

Browse files
feat(dir-sdk-python): add examples (#7)
Signed-off-by: Bendegúz Csirmaz <csirmazbendeguz@gmail.com>
1 parent 93ad0a5 commit 2d2ec9d

8 files changed

Lines changed: 839 additions & 44 deletions

File tree

Taskfile.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ tasks:
7474
'{{.UV_BIN}}' run pytest
7575
7676
# Run SDK example to verify it works
77-
# cd {{ .ROOT_DIR }}/sdk/examples/example-py
78-
# '{{.UV_BIN}}' sync
79-
# '{{.UV_BIN}}' run python example.py
77+
cd {{ .ROOT_DIR }}/examples
78+
'{{ .UV_BIN }}' sync
79+
'{{ .UV_BIN }}' run python example.py
8080
8181
sdk:deps:python:
8282
desc: Install deps for python SDK package

examples/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Python
2+
dist/
3+
__pycache__/
4+
*.egg-info/
5+
*.pyc
6+
*.pyo
7+
*.pyd
8+
9+
# Virtual Environments
10+
.venv/

examples/.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.14

examples/example.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Copyright AGNTCY Contributors (https://github.com/agntcy)
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from google.protobuf.json_format import MessageToJson
5+
6+
from agntcy.dir_sdk.client import Client
7+
from agntcy.dir_sdk.models import core_v1, search_v1, routing_v1
8+
9+
10+
def generate_record(name):
11+
return core_v1.Record(
12+
data={
13+
"name": name,
14+
"version": "v1.0.0",
15+
"schema_version": "0.8.0",
16+
"description": "My example agent",
17+
"authors": ["AGNTCY"],
18+
"created_at": "2025-03-19T17:06:37Z",
19+
"skills": [
20+
{
21+
"name": "natural_language_processing/natural_language_generation/text_completion",
22+
"id": 10201
23+
},
24+
{
25+
"name": "natural_language_processing/analytical_reasoning/problem_solving",
26+
"id": 10702
27+
}
28+
],
29+
"locators": [
30+
{
31+
"type": "docker_image",
32+
"url": "https://ghcr.io/agntcy/marketing-strategy"
33+
}
34+
],
35+
"domains": [
36+
{
37+
"name": "technology/networking",
38+
"id": 103
39+
}
40+
],
41+
"modules": [
42+
{
43+
"name": "integration/a2a",
44+
"id": 203,
45+
"data": {
46+
"protocol_version": "lightweight orchestra moral",
47+
"card_data": "centres",
48+
"capabilities": [
49+
"state_transition_history",
50+
"push_notifications"
51+
],
52+
"transports": [
53+
"grpc",
54+
"http"
55+
],
56+
"output_modes": [
57+
"text/html"
58+
]
59+
}
60+
}
61+
]
62+
},
63+
)
64+
65+
66+
def main() -> None:
67+
client = Client()
68+
69+
records = [generate_record(x) for x in ["example-record", "example-record2"]]
70+
71+
# Push objects to the store
72+
refs = client.push(records)
73+
74+
for ref in refs:
75+
print("Pushed object ref:", ref.cid)
76+
77+
# Pull objects from the store
78+
pulled_records = client.pull(refs)
79+
80+
for pulled_record in pulled_records:
81+
print("Pulled object data:", MessageToJson(pulled_record))
82+
83+
# Lookup the object
84+
metadatas = client.lookup(refs)
85+
86+
for metadata in metadatas:
87+
print("Lookup object metadata:", MessageToJson(metadata))
88+
89+
# Publish the object
90+
record_refs = routing_v1.RecordRefs(refs=[refs[0]])
91+
publish_request = routing_v1.PublishRequest(record_refs=record_refs)
92+
client.publish(publish_request)
93+
print("Object published.")
94+
95+
# List objects in the store
96+
query = routing_v1.RecordQuery(
97+
type=routing_v1.RECORD_QUERY_TYPE_SKILL,
98+
value="/skills/Natural Language Processing/Text Completion",
99+
)
100+
101+
list_request = routing_v1.ListRequest(queries=[query])
102+
objects = list(client.list(list_request))
103+
104+
for o in objects:
105+
print("Listed object:", MessageToJson(o))
106+
107+
# Search objects
108+
search_query = search_v1.RecordQuery(
109+
type=search_v1.RECORD_QUERY_TYPE_VERSION, value="v1.*",
110+
)
111+
112+
search_request = search_v1.SearchCIDsRequest(queries=[search_query], limit=3)
113+
objects = list(client.search_cids(search_request))
114+
115+
print("Searched objects:",objects)
116+
117+
# Unpublish the object
118+
record_refs = routing_v1.RecordRefs(refs=[refs[0]])
119+
unpublish_request = routing_v1.UnpublishRequest(record_refs=record_refs)
120+
client.unpublish(unpublish_request)
121+
print("Object unpublished.")
122+
123+
# Delete the object
124+
client.delete(refs)
125+
print("Objects are deleted.")
126+
127+
128+
if __name__ == "__main__":
129+
main()
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Copyright AGNTCY Contributors (https://github.com/agntcy)
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from __future__ import annotations
5+
6+
import argparse
7+
import os
8+
9+
from agntcy.dir_sdk.client import Client, Config, OAuthPkceError
10+
from agntcy.dir_sdk.models import search_v1
11+
12+
DEFAULT_OIDC_ISSUER = "https://dev.idp.ads.outshift.io"
13+
DEFAULT_SERVER_ADDRESS = "dev.gateway.ads.outshift.io:443"
14+
DEFAULT_TLS_SERVER_NAME = "dev.gateway.ads.outshift.io"
15+
DEFAULT_REDIRECT_URI = "http://localhost:8484/callback"
16+
17+
18+
def require_env(name: str) -> str:
19+
value = os.environ.get(name, "").strip()
20+
if not value:
21+
msg = f"{name} is required for the interactive OIDC example"
22+
raise RuntimeError(msg)
23+
return value
24+
25+
26+
def parse_args() -> argparse.Namespace:
27+
parser = argparse.ArgumentParser(
28+
description="Interactive OIDC example that calls SearchCIDs only.",
29+
)
30+
parser.add_argument(
31+
"--version",
32+
default="v1*",
33+
help="Version query used for SearchCIDs (default: v1*)",
34+
)
35+
parser.add_argument(
36+
"--limit",
37+
type=int,
38+
default=3,
39+
help="Maximum number of CIDs to return (default: 3)",
40+
)
41+
return parser.parse_args()
42+
43+
44+
def build_client() -> Client:
45+
config = Config(
46+
server_address=os.environ.get(
47+
"DIRECTORY_CLIENT_SERVER_ADDRESS",
48+
DEFAULT_SERVER_ADDRESS,
49+
),
50+
auth_mode="oidc",
51+
oidc_issuer=DEFAULT_OIDC_ISSUER,
52+
oidc_client_id=require_env("DIRECTORY_CLIENT_OIDC_CLIENT_ID"),
53+
oidc_client_secret=os.environ.get("DIRECTORY_CLIENT_OIDC_CLIENT_SECRET", ""),
54+
tls_server_name=os.environ.get(
55+
"DIRECTORY_CLIENT_TLS_SERVER_NAME",
56+
DEFAULT_TLS_SERVER_NAME,
57+
),
58+
oidc_redirect_uri=os.environ.get(
59+
"DIRECTORY_CLIENT_OIDC_REDIRECT_URI",
60+
DEFAULT_REDIRECT_URI,
61+
),
62+
oidc_callback_port=int(
63+
os.environ.get("DIRECTORY_CLIENT_OIDC_CALLBACK_PORT", "8484"),
64+
),
65+
)
66+
client = Client(config)
67+
holder = getattr(client, "_oauth_holder", None)
68+
if holder is not None:
69+
try:
70+
holder.get_access_token()
71+
print("Using cached OIDC token.")
72+
return client
73+
except RuntimeError:
74+
pass
75+
76+
print("No cached OIDC token found. Starting interactive login.")
77+
client.authenticate_oauth_pkce()
78+
return client
79+
80+
81+
def main() -> None:
82+
args = parse_args()
83+
client = build_client()
84+
85+
search_query = search_v1.RecordQuery(
86+
type=search_v1.RECORD_QUERY_TYPE_VERSION,
87+
value=args.version,
88+
)
89+
search_request = search_v1.SearchCIDsRequest(
90+
queries=[search_query],
91+
limit=args.limit,
92+
)
93+
objects = list(client.search_cids(search_request))
94+
print(f"SearchCIDs results for version {args.version!r}:")
95+
for obj in objects:
96+
print(obj)
97+
98+
99+
if __name__ == "__main__":
100+
try:
101+
main()
102+
except OAuthPkceError as e:
103+
print(f"Interactive OIDC login failed: {e}")
104+
raise

examples/pyproject.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[project]
2+
name = "dir-example"
3+
version = "0.0.0"
4+
requires-python = ">=3.10"
5+
dependencies = [
6+
"agntcy-dir",
7+
"httpx>=0.28.1",
8+
]
9+
10+
[[tool.uv.index]]
11+
url = "https://buf.build/gen/python"
12+
13+
[tool.uv.sources]
14+
agntcy-dir = { path = "..", editable = true }

0 commit comments

Comments
 (0)