camelai-run 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: camelai-run
3
+ Version: 0.7.0
4
+ Summary: Python SDK for camelRun
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://agents.camelai.dev
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: httpx==0.28.1
10
+ Provides-Extra: server
11
+ Requires-Dist: cryptography>=42; extra == "server"
12
+
13
+ # camelai-run
14
+
15
+ The Python SDK for camelRun: durable agents you upsert by key
16
+ and run, with tools that are ordinary functions in your code. The runtime runs
17
+ the model loop, keeps each agent's history and files, and runs model-written
18
+ code in a sandbox that can only call your tools.
19
+
20
+ ```sh
21
+ pip install camelai-run
22
+ ```
23
+
24
+ Python 3.11 or later. Get an API key from the console at
25
+ <https://agents.camelai.dev/console> and export it as `CAMELAI_API_KEY`.
26
+
27
+ ```python
28
+ import asyncio
29
+ from camelai_run import Agents, tool
30
+
31
+ @tool
32
+ def weather(city: str) -> dict:
33
+ """Today's weather in a city"""
34
+ return {"city": city, "forecast": "sunny", "highC": 24} # runs here, in your process
35
+
36
+ async def main():
37
+ async with Agents() as agents:
38
+ agent = await agents.upsert("quickstart", model="anthropic/claude-sonnet-5-5",
39
+ instructions="You are a concise assistant.", tools=[weather])
40
+ run = await agent.run("Should I bring an umbrella in Lisbon today?")
41
+ print(run.text)
42
+
43
+ asyncio.run(main())
44
+ ```
45
+
46
+ - **Keyed agents.** `upsert(key, ...)` makes the agent for your key, or brings the
47
+ existing one to the configuration given; its history and files last until you
48
+ delete it.
49
+ - **Runs.** `run()` returns a `Run` (`status`, `text`, `inputs`, `error`,
50
+ `tool_errors`) and raises `RunError` on failure (unless `throw_on_error=False`).
51
+ No timeout unless you pass `timeout=`. `agent.stream()` yields text, tool calls
52
+ and results as they happen, then the run.
53
+ - **Tools.** `@tool` takes async or plain functions (plain ones run in a thread),
54
+ `timeout=` in seconds, and `needs_approval=True`. `context.idempotency_key` is
55
+ stable across retries; `context.progress("...")` reports progress.
56
+ - **People in the loop.** `await run.inputs[0].answer(True, from_="alice")` resumes
57
+ a run waiting on approval.
58
+ - **Events.** `on_event` may be a plain or an async function; it runs in order,
59
+ apart from the connection.
60
+
61
+ Documentation: [Quickstart](https://agents.camelai.dev/docs/quickstart.md),
62
+ [Concepts](https://agents.camelai.dev/docs/concepts.md),
63
+ [SDK reference](https://agents.camelai.dev/docs/reference/sdk.md),
64
+ and all of it as Markdown at <https://agents.camelai.dev/llms.txt>.
65
+
66
+ ## Serving tools to many users
67
+
68
+ When one server answers tools for many users' agents, serve them over HTTP and let
69
+ the runtime say who each call is for. `serve_tools` is an ASGI app that verifies the
70
+ runtime's signed identity token on every request and hands each call a
71
+ `context.identity`:
72
+
73
+ ```sh
74
+ pip install "camelai-run[server]"
75
+ ```
76
+
77
+ ```python
78
+ from camelai_run import ToolContext, serve_tools, tool
79
+
80
+ @tool
81
+ async def list_todos(context: ToolContext) -> dict:
82
+ """The current user's to-dos"""
83
+ who = context.identity # user (the actor, else the agent's subject), subject, tenant, agent, context
84
+ return {"todos": await db.todos(user=who.user, team=who.context["team"])}
85
+
86
+ # tenant: yours (GET /v1/me): tokens for other tenants' agents, which may claim any user, are refused.
87
+ app = serve_tools([list_todos], runtime="https://agents.camelai.dev", tenant="acme") # uvicorn, or mount in FastAPI
88
+ ```
89
+
90
+ Name the server in a definition with `mcpServers=[{"name": "todos", "url": ..., "auth": {"type": "runtime"}}]`,
91
+ create agents with `subject=` and `context=`, and run them with `user=`.
92
+ The same `@tool` functions get the same identity when attached to an agent.
93
+ `verify_runtime_token(token, runtime=..., tenant=..., audience=...)` checks a token on its own,
94
+ and `TestRuntime()` signs tokens for tests: `await TestRuntime().call_tool(app, url, "list_todos", {}, subject="alice")`.
95
+
96
+ Keep the API key on your backend: it can create and control every
97
+ agent in your tenant. Sign in at https://agents.camelai.dev/console to add provider
98
+ keys, create API tokens and watch agents. The TypeScript SDK is
99
+ [`@camelai/run`](https://www.npmjs.com/package/@camelai/run).
100
+
101
+ ## Asking the user
102
+
103
+ A tool marked `@tool(needs_approval=True)` is approved before each call; inside a
104
+ tool, `context.confirm(message)`, `context.ask(message, schema)` and
105
+ `context.require_url(url, message)` ask the user. The run then returns with
106
+ `status == "input_required"`, and answering its inputs resumes it:
107
+
108
+ ```python
109
+ @tool
110
+ async def delete_app(app: str, context: ToolContext) -> dict:
111
+ """Delete an app"""
112
+ # Ask first: the call ends here, and runs again with the answer.
113
+ if not await context.confirm(f"Delete {app}? Its URL stops working."):
114
+ return {"cancelled": True}
115
+ return await apps.delete(app, idempotency_key=context.idempotency_key)
116
+
117
+ agent = await agents.upsert("ops", tools=[delete_app])
118
+ run = await agent.run("Delete the demo app", user="alice")
119
+ while run.status == "input_required":
120
+ run = await run.inputs[0].answer(True, from_="alice")
121
+ ```
122
+
123
+ Everything in a tool before an ask runs again when the user answers.
124
+ `agent.pending_inputs()` lists what an agent waits on, and
125
+ `agents.runtime.inbox(state="pending")` what all your agents do.
@@ -0,0 +1,113 @@
1
+ # camelai-run
2
+
3
+ The Python SDK for camelRun: durable agents you upsert by key
4
+ and run, with tools that are ordinary functions in your code. The runtime runs
5
+ the model loop, keeps each agent's history and files, and runs model-written
6
+ code in a sandbox that can only call your tools.
7
+
8
+ ```sh
9
+ pip install camelai-run
10
+ ```
11
+
12
+ Python 3.11 or later. Get an API key from the console at
13
+ <https://agents.camelai.dev/console> and export it as `CAMELAI_API_KEY`.
14
+
15
+ ```python
16
+ import asyncio
17
+ from camelai_run import Agents, tool
18
+
19
+ @tool
20
+ def weather(city: str) -> dict:
21
+ """Today's weather in a city"""
22
+ return {"city": city, "forecast": "sunny", "highC": 24} # runs here, in your process
23
+
24
+ async def main():
25
+ async with Agents() as agents:
26
+ agent = await agents.upsert("quickstart", model="anthropic/claude-sonnet-5-5",
27
+ instructions="You are a concise assistant.", tools=[weather])
28
+ run = await agent.run("Should I bring an umbrella in Lisbon today?")
29
+ print(run.text)
30
+
31
+ asyncio.run(main())
32
+ ```
33
+
34
+ - **Keyed agents.** `upsert(key, ...)` makes the agent for your key, or brings the
35
+ existing one to the configuration given; its history and files last until you
36
+ delete it.
37
+ - **Runs.** `run()` returns a `Run` (`status`, `text`, `inputs`, `error`,
38
+ `tool_errors`) and raises `RunError` on failure (unless `throw_on_error=False`).
39
+ No timeout unless you pass `timeout=`. `agent.stream()` yields text, tool calls
40
+ and results as they happen, then the run.
41
+ - **Tools.** `@tool` takes async or plain functions (plain ones run in a thread),
42
+ `timeout=` in seconds, and `needs_approval=True`. `context.idempotency_key` is
43
+ stable across retries; `context.progress("...")` reports progress.
44
+ - **People in the loop.** `await run.inputs[0].answer(True, from_="alice")` resumes
45
+ a run waiting on approval.
46
+ - **Events.** `on_event` may be a plain or an async function; it runs in order,
47
+ apart from the connection.
48
+
49
+ Documentation: [Quickstart](https://agents.camelai.dev/docs/quickstart.md),
50
+ [Concepts](https://agents.camelai.dev/docs/concepts.md),
51
+ [SDK reference](https://agents.camelai.dev/docs/reference/sdk.md),
52
+ and all of it as Markdown at <https://agents.camelai.dev/llms.txt>.
53
+
54
+ ## Serving tools to many users
55
+
56
+ When one server answers tools for many users' agents, serve them over HTTP and let
57
+ the runtime say who each call is for. `serve_tools` is an ASGI app that verifies the
58
+ runtime's signed identity token on every request and hands each call a
59
+ `context.identity`:
60
+
61
+ ```sh
62
+ pip install "camelai-run[server]"
63
+ ```
64
+
65
+ ```python
66
+ from camelai_run import ToolContext, serve_tools, tool
67
+
68
+ @tool
69
+ async def list_todos(context: ToolContext) -> dict:
70
+ """The current user's to-dos"""
71
+ who = context.identity # user (the actor, else the agent's subject), subject, tenant, agent, context
72
+ return {"todos": await db.todos(user=who.user, team=who.context["team"])}
73
+
74
+ # tenant: yours (GET /v1/me): tokens for other tenants' agents, which may claim any user, are refused.
75
+ app = serve_tools([list_todos], runtime="https://agents.camelai.dev", tenant="acme") # uvicorn, or mount in FastAPI
76
+ ```
77
+
78
+ Name the server in a definition with `mcpServers=[{"name": "todos", "url": ..., "auth": {"type": "runtime"}}]`,
79
+ create agents with `subject=` and `context=`, and run them with `user=`.
80
+ The same `@tool` functions get the same identity when attached to an agent.
81
+ `verify_runtime_token(token, runtime=..., tenant=..., audience=...)` checks a token on its own,
82
+ and `TestRuntime()` signs tokens for tests: `await TestRuntime().call_tool(app, url, "list_todos", {}, subject="alice")`.
83
+
84
+ Keep the API key on your backend: it can create and control every
85
+ agent in your tenant. Sign in at https://agents.camelai.dev/console to add provider
86
+ keys, create API tokens and watch agents. The TypeScript SDK is
87
+ [`@camelai/run`](https://www.npmjs.com/package/@camelai/run).
88
+
89
+ ## Asking the user
90
+
91
+ A tool marked `@tool(needs_approval=True)` is approved before each call; inside a
92
+ tool, `context.confirm(message)`, `context.ask(message, schema)` and
93
+ `context.require_url(url, message)` ask the user. The run then returns with
94
+ `status == "input_required"`, and answering its inputs resumes it:
95
+
96
+ ```python
97
+ @tool
98
+ async def delete_app(app: str, context: ToolContext) -> dict:
99
+ """Delete an app"""
100
+ # Ask first: the call ends here, and runs again with the answer.
101
+ if not await context.confirm(f"Delete {app}? Its URL stops working."):
102
+ return {"cancelled": True}
103
+ return await apps.delete(app, idempotency_key=context.idempotency_key)
104
+
105
+ agent = await agents.upsert("ops", tools=[delete_app])
106
+ run = await agent.run("Delete the demo app", user="alice")
107
+ while run.status == "input_required":
108
+ run = await run.inputs[0].answer(True, from_="alice")
109
+ ```
110
+
111
+ Everything in a tool before an ask runs again when the user answers.
112
+ `agent.pending_inputs()` lists what an agent waits on, and
113
+ `agents.runtime.inbox(state="pending")` what all your agents do.
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: camelai-run
3
+ Version: 0.7.0
4
+ Summary: Python SDK for camelRun
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://agents.camelai.dev
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: httpx==0.28.1
10
+ Provides-Extra: server
11
+ Requires-Dist: cryptography>=42; extra == "server"
12
+
13
+ # camelai-run
14
+
15
+ The Python SDK for camelRun: durable agents you upsert by key
16
+ and run, with tools that are ordinary functions in your code. The runtime runs
17
+ the model loop, keeps each agent's history and files, and runs model-written
18
+ code in a sandbox that can only call your tools.
19
+
20
+ ```sh
21
+ pip install camelai-run
22
+ ```
23
+
24
+ Python 3.11 or later. Get an API key from the console at
25
+ <https://agents.camelai.dev/console> and export it as `CAMELAI_API_KEY`.
26
+
27
+ ```python
28
+ import asyncio
29
+ from camelai_run import Agents, tool
30
+
31
+ @tool
32
+ def weather(city: str) -> dict:
33
+ """Today's weather in a city"""
34
+ return {"city": city, "forecast": "sunny", "highC": 24} # runs here, in your process
35
+
36
+ async def main():
37
+ async with Agents() as agents:
38
+ agent = await agents.upsert("quickstart", model="anthropic/claude-sonnet-5-5",
39
+ instructions="You are a concise assistant.", tools=[weather])
40
+ run = await agent.run("Should I bring an umbrella in Lisbon today?")
41
+ print(run.text)
42
+
43
+ asyncio.run(main())
44
+ ```
45
+
46
+ - **Keyed agents.** `upsert(key, ...)` makes the agent for your key, or brings the
47
+ existing one to the configuration given; its history and files last until you
48
+ delete it.
49
+ - **Runs.** `run()` returns a `Run` (`status`, `text`, `inputs`, `error`,
50
+ `tool_errors`) and raises `RunError` on failure (unless `throw_on_error=False`).
51
+ No timeout unless you pass `timeout=`. `agent.stream()` yields text, tool calls
52
+ and results as they happen, then the run.
53
+ - **Tools.** `@tool` takes async or plain functions (plain ones run in a thread),
54
+ `timeout=` in seconds, and `needs_approval=True`. `context.idempotency_key` is
55
+ stable across retries; `context.progress("...")` reports progress.
56
+ - **People in the loop.** `await run.inputs[0].answer(True, from_="alice")` resumes
57
+ a run waiting on approval.
58
+ - **Events.** `on_event` may be a plain or an async function; it runs in order,
59
+ apart from the connection.
60
+
61
+ Documentation: [Quickstart](https://agents.camelai.dev/docs/quickstart.md),
62
+ [Concepts](https://agents.camelai.dev/docs/concepts.md),
63
+ [SDK reference](https://agents.camelai.dev/docs/reference/sdk.md),
64
+ and all of it as Markdown at <https://agents.camelai.dev/llms.txt>.
65
+
66
+ ## Serving tools to many users
67
+
68
+ When one server answers tools for many users' agents, serve them over HTTP and let
69
+ the runtime say who each call is for. `serve_tools` is an ASGI app that verifies the
70
+ runtime's signed identity token on every request and hands each call a
71
+ `context.identity`:
72
+
73
+ ```sh
74
+ pip install "camelai-run[server]"
75
+ ```
76
+
77
+ ```python
78
+ from camelai_run import ToolContext, serve_tools, tool
79
+
80
+ @tool
81
+ async def list_todos(context: ToolContext) -> dict:
82
+ """The current user's to-dos"""
83
+ who = context.identity # user (the actor, else the agent's subject), subject, tenant, agent, context
84
+ return {"todos": await db.todos(user=who.user, team=who.context["team"])}
85
+
86
+ # tenant: yours (GET /v1/me): tokens for other tenants' agents, which may claim any user, are refused.
87
+ app = serve_tools([list_todos], runtime="https://agents.camelai.dev", tenant="acme") # uvicorn, or mount in FastAPI
88
+ ```
89
+
90
+ Name the server in a definition with `mcpServers=[{"name": "todos", "url": ..., "auth": {"type": "runtime"}}]`,
91
+ create agents with `subject=` and `context=`, and run them with `user=`.
92
+ The same `@tool` functions get the same identity when attached to an agent.
93
+ `verify_runtime_token(token, runtime=..., tenant=..., audience=...)` checks a token on its own,
94
+ and `TestRuntime()` signs tokens for tests: `await TestRuntime().call_tool(app, url, "list_todos", {}, subject="alice")`.
95
+
96
+ Keep the API key on your backend: it can create and control every
97
+ agent in your tenant. Sign in at https://agents.camelai.dev/console to add provider
98
+ keys, create API tokens and watch agents. The TypeScript SDK is
99
+ [`@camelai/run`](https://www.npmjs.com/package/@camelai/run).
100
+
101
+ ## Asking the user
102
+
103
+ A tool marked `@tool(needs_approval=True)` is approved before each call; inside a
104
+ tool, `context.confirm(message)`, `context.ask(message, schema)` and
105
+ `context.require_url(url, message)` ask the user. The run then returns with
106
+ `status == "input_required"`, and answering its inputs resumes it:
107
+
108
+ ```python
109
+ @tool
110
+ async def delete_app(app: str, context: ToolContext) -> dict:
111
+ """Delete an app"""
112
+ # Ask first: the call ends here, and runs again with the answer.
113
+ if not await context.confirm(f"Delete {app}? Its URL stops working."):
114
+ return {"cancelled": True}
115
+ return await apps.delete(app, idempotency_key=context.idempotency_key)
116
+
117
+ agent = await agents.upsert("ops", tools=[delete_app])
118
+ run = await agent.run("Delete the demo app", user="alice")
119
+ while run.status == "input_required":
120
+ run = await run.inputs[0].answer(True, from_="alice")
121
+ ```
122
+
123
+ Everything in a tool before an ask runs again when the user answers.
124
+ `agent.pending_inputs()` lists what an agent waits on, and
125
+ `agents.runtime.inbox(state="pending")` what all your agents do.
@@ -0,0 +1,8 @@
1
+ README.md
2
+ camelai_run.py
3
+ pyproject.toml
4
+ camelai_run.egg-info/PKG-INFO
5
+ camelai_run.egg-info/SOURCES.txt
6
+ camelai_run.egg-info/dependency_links.txt
7
+ camelai_run.egg-info/requires.txt
8
+ camelai_run.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ httpx==0.28.1
2
+
3
+ [server]
4
+ cryptography>=42
@@ -0,0 +1 @@
1
+ camelai_run