tars-sdk 0.1.1__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.
- tars_sdk-0.1.1/.gitignore +56 -0
- tars_sdk-0.1.1/CHANGELOG.md +45 -0
- tars_sdk-0.1.1/PKG-INFO +91 -0
- tars_sdk-0.1.1/README.md +65 -0
- tars_sdk-0.1.1/pyproject.toml +48 -0
- tars_sdk-0.1.1/src/tars_sdk/__init__.py +219 -0
- tars_sdk-0.1.1/src/tars_sdk/_sse.py +33 -0
- tars_sdk-0.1.1/src/tars_sdk/_wire.py +49 -0
- tars_sdk-0.1.1/src/tars_sdk/client.py +738 -0
- tars_sdk-0.1.1/src/tars_sdk/errors.py +34 -0
- tars_sdk-0.1.1/src/tars_sdk/events.py +284 -0
- tars_sdk-0.1.1/src/tars_sdk/models.py +696 -0
- tars_sdk-0.1.1/src/tars_sdk/py.typed +0 -0
- tars_sdk-0.1.1/src/tars_sdk/session.py +119 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Node
|
|
2
|
+
node_modules/
|
|
3
|
+
*.tsbuildinfo
|
|
4
|
+
*.log
|
|
5
|
+
|
|
6
|
+
# Environment / secrets — commit only the .example files
|
|
7
|
+
.env
|
|
8
|
+
.env.*
|
|
9
|
+
!.env.example
|
|
10
|
+
|
|
11
|
+
# Terraform — providers, state, and real tfvars (contain subscription IDs)
|
|
12
|
+
**/.terraform/
|
|
13
|
+
*.tfstate
|
|
14
|
+
*.tfstate.*
|
|
15
|
+
terraform.tfvars
|
|
16
|
+
!terraform.tfvars.example
|
|
17
|
+
|
|
18
|
+
# Restate local data (journals, ledger)
|
|
19
|
+
restate-data/
|
|
20
|
+
|
|
21
|
+
# OS
|
|
22
|
+
.DS_Store
|
|
23
|
+
|
|
24
|
+
# Terraform saved plans
|
|
25
|
+
tfplan
|
|
26
|
+
|
|
27
|
+
# built SDK artifacts
|
|
28
|
+
sdk/dist/
|
|
29
|
+
|
|
30
|
+
# Next.js
|
|
31
|
+
.next/
|
|
32
|
+
backend/dist/
|
|
33
|
+
frontend/.next/
|
|
34
|
+
frontend/next-env.d.ts
|
|
35
|
+
website/next-env.d.ts
|
|
36
|
+
website/package-lock.json
|
|
37
|
+
|
|
38
|
+
# Claude Code — machine-local permission state and per-task worktrees
|
|
39
|
+
# (EnterWorktree). settings.json IS tracked; settings.local.json is not.
|
|
40
|
+
.claude/settings.local.json
|
|
41
|
+
.claude/worktrees/
|
|
42
|
+
|
|
43
|
+
# Verification screenshots never belong at the repo root
|
|
44
|
+
/*.png
|
|
45
|
+
/*.jpg
|
|
46
|
+
|
|
47
|
+
# .NET build output (sdk-dotnet, examples/chat-dotnet)
|
|
48
|
+
bin/
|
|
49
|
+
obj/
|
|
50
|
+
|
|
51
|
+
# Python (sdk-python, examples/chat-python)
|
|
52
|
+
.venv/
|
|
53
|
+
__pycache__/
|
|
54
|
+
*.egg-info/
|
|
55
|
+
sdk-python/dist/
|
|
56
|
+
.pytest_cache/
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# tars-sdk
|
|
2
|
+
|
|
3
|
+
Tenant-facing record of the Python SDK, newest first. Each entry names the
|
|
4
|
+
minimum TARS backend it needs. The version lives in `pyproject.toml`; bump
|
|
5
|
+
it in the same PR as the entry, and label that PR `release-approved` once
|
|
6
|
+
prd runs that backend — merging it publishes.
|
|
7
|
+
|
|
8
|
+
## 0.1.1
|
|
9
|
+
|
|
10
|
+
First published release. Needs TARS backend ≥ `prd-20261108-2059-sha-918f00a7`.
|
|
11
|
+
|
|
12
|
+
- `TarsClient(api_key, base_url=, provider_keys=, act_as_tenant=, http_client=)`:
|
|
13
|
+
BYOK provider keys as one `x-tars-key-<provider>` header per key, never
|
|
14
|
+
stored; `act_as_tenant` for admin keys (read-only); an injectable
|
|
15
|
+
`httpx.AsyncClient`. Async context manager; `aclose()`.
|
|
16
|
+
- `agents`: create, list, get, update, archive, unarchive, versions. On
|
|
17
|
+
`update`, omitted keywords are unchanged and `None` clears a clearable
|
|
18
|
+
field (description, tools, metadata, mcp_servers, skills, environment,
|
|
19
|
+
reasoning).
|
|
20
|
+
- `sessions`: create (with client-executed tools), open, list, get, events,
|
|
21
|
+
rename, set_mcp_servers, set_custom_tools, interrupt, compact, usage,
|
|
22
|
+
destroy.
|
|
23
|
+
- `TarsSession`: `send` (streams the turn and runs your tool handlers, sync
|
|
24
|
+
or async), `send_raw`, `stream` (replay + live tail), `resume` (current
|
|
25
|
+
turn only), `mount_file`, `attach_file`, `interrupt`, `compact`, `destroy`.
|
|
26
|
+
- `files`: upload, upload_text, list, get, download (stream), download_bytes.
|
|
27
|
+
- `skills`: create, list, get, versions, push_version, download_version.
|
|
28
|
+
- `mcp_credentials`: set (`BearerCredential` / `OAuthCredential`), list,
|
|
29
|
+
delete; `mcp.list_tools`.
|
|
30
|
+
- `usage`: get (from/to/session/end-user filters), daily, sessions. Dates
|
|
31
|
+
are `datetime` (naive = UTC) or ISO strings, sent as UTC ISO 8601.
|
|
32
|
+
- `api_keys`: create (key returned once), list, revoke.
|
|
33
|
+
- `completions.create`; `admin` (admin keys): tenants, their keys, stats.
|
|
34
|
+
- `me`, `providers`, `provider_status`, `models`.
|
|
35
|
+
- Typed events: one dataclass per journal event type, `UnknownEvent` for
|
|
36
|
+
types newer than the SDK, `raw` on every event. `SESSION_EVENT_TYPES` and
|
|
37
|
+
`AGENT_ERROR_CODES` are pinned to the backend taxonomy by a test.
|
|
38
|
+
- `TarsApiError` with `status`, the stable `code` from the error envelope,
|
|
39
|
+
and the raw `body`.
|
|
40
|
+
|
|
41
|
+
## 0.1.0
|
|
42
|
+
|
|
43
|
+
Never published: PyPI refused the `tars_sdk-0.1.0` filenames because an
|
|
44
|
+
unrelated, since-deleted upload had used them once, and PyPI never lets a
|
|
45
|
+
filename be reused. 0.1.1 is the same code.
|
tars_sdk-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tars-sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Client SDK for TARS — the self-hosted managed-agents platform (agents, durable sessions, streaming turns, client-executed tools, files).
|
|
5
|
+
Project-URL: Homepage, https://github.com/LegendariTech/tars
|
|
6
|
+
Project-URL: Repository, https://github.com/LegendariTech/tars
|
|
7
|
+
Project-URL: Changelog, https://github.com/LegendariTech/tars/blob/dev/sdk-python/CHANGELOG.md
|
|
8
|
+
Author: Legendari
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: agents,ai,anthropic,mcp,sdk,tars
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: httpx<1,>=0.27
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# tars-sdk
|
|
28
|
+
|
|
29
|
+
Python client for [TARS](https://github.com/LegendariTech/tars) — a
|
|
30
|
+
self-hosted managed-agents platform: server-side agent loops in sandboxes,
|
|
31
|
+
an append-only session event log with SSE streaming + replay, client-executed
|
|
32
|
+
tools, files, and usage metering.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install tars-sdk
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Python 3.10+, async (`asyncio`), one dependency (`httpx`).
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import asyncio, os
|
|
44
|
+
from tars_sdk import TarsClient, TextDeltaEvent
|
|
45
|
+
|
|
46
|
+
async def main():
|
|
47
|
+
async with TarsClient(
|
|
48
|
+
api_key=os.environ["TARS_API_KEY"], # tars_sk_…
|
|
49
|
+
base_url="https://your-tars-host",
|
|
50
|
+
provider_keys={"openrouter": os.environ["OPENROUTER_API_KEY"]},
|
|
51
|
+
) as tars:
|
|
52
|
+
agent = await tars.agents.create(
|
|
53
|
+
name="assistant",
|
|
54
|
+
system="You are a helpful assistant.",
|
|
55
|
+
model="openrouter/anthropic/claude-sonnet-5",
|
|
56
|
+
)
|
|
57
|
+
session = await tars.sessions.create(agent_id=agent.id)
|
|
58
|
+
async for ev in session.send("Summarize the attached file."):
|
|
59
|
+
if isinstance(ev, TextDeltaEvent):
|
|
60
|
+
print(ev.text, end="", flush=True)
|
|
61
|
+
|
|
62
|
+
asyncio.run(main())
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Streaming surfaces
|
|
66
|
+
|
|
67
|
+
- `session.send(text)` — yields the turn's events; client-executed tools
|
|
68
|
+
you registered (`tools=` on `sessions.create` / `session.on_tool_call`)
|
|
69
|
+
are run and their results returned to the turn automatically. Handlers
|
|
70
|
+
may be sync or async.
|
|
71
|
+
- `session.send_raw(text)` — same events, **no auto-dispatch**: hosts
|
|
72
|
+
that own their tool dispatcher consume this.
|
|
73
|
+
- `session.stream(after)` — replay the journal after a seq, then
|
|
74
|
+
live-tail; survives restarts.
|
|
75
|
+
- `session.resume(after)` — reconnect to the current turn only.
|
|
76
|
+
|
|
77
|
+
Every event is a `SessionEvent` dataclass (`TextDeltaEvent`,
|
|
78
|
+
`ToolCallEvent`, `TurnEndEvent`, …); a type newer than this SDK arrives as
|
|
79
|
+
`UnknownEvent` with the payload in `raw`. Non-2xx answers raise
|
|
80
|
+
`TarsApiError` with `status` and the stable `code`.
|
|
81
|
+
|
|
82
|
+
## Everything else
|
|
83
|
+
|
|
84
|
+
`tars.skills`, `tars.mcp_credentials`, `tars.mcp.list_tools`, `tars.usage`,
|
|
85
|
+
`tars.api_keys`, `tars.completions` and `tars.admin` (admin keys) cover the
|
|
86
|
+
rest of the API, one typed method per `/v1/*` route. On
|
|
87
|
+
`agents.update`, a keyword you do not pass is unchanged; passing `None` for
|
|
88
|
+
a clearable field (description, tools, metadata, mcp_servers, skills,
|
|
89
|
+
environment, reasoning) clears it.
|
|
90
|
+
|
|
91
|
+
Full docs: the TARS docs site, *SDK for Python* page.
|
tars_sdk-0.1.1/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# tars-sdk
|
|
2
|
+
|
|
3
|
+
Python client for [TARS](https://github.com/LegendariTech/tars) — a
|
|
4
|
+
self-hosted managed-agents platform: server-side agent loops in sandboxes,
|
|
5
|
+
an append-only session event log with SSE streaming + replay, client-executed
|
|
6
|
+
tools, files, and usage metering.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install tars-sdk
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Python 3.10+, async (`asyncio`), one dependency (`httpx`).
|
|
13
|
+
|
|
14
|
+
## Quickstart
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import asyncio, os
|
|
18
|
+
from tars_sdk import TarsClient, TextDeltaEvent
|
|
19
|
+
|
|
20
|
+
async def main():
|
|
21
|
+
async with TarsClient(
|
|
22
|
+
api_key=os.environ["TARS_API_KEY"], # tars_sk_…
|
|
23
|
+
base_url="https://your-tars-host",
|
|
24
|
+
provider_keys={"openrouter": os.environ["OPENROUTER_API_KEY"]},
|
|
25
|
+
) as tars:
|
|
26
|
+
agent = await tars.agents.create(
|
|
27
|
+
name="assistant",
|
|
28
|
+
system="You are a helpful assistant.",
|
|
29
|
+
model="openrouter/anthropic/claude-sonnet-5",
|
|
30
|
+
)
|
|
31
|
+
session = await tars.sessions.create(agent_id=agent.id)
|
|
32
|
+
async for ev in session.send("Summarize the attached file."):
|
|
33
|
+
if isinstance(ev, TextDeltaEvent):
|
|
34
|
+
print(ev.text, end="", flush=True)
|
|
35
|
+
|
|
36
|
+
asyncio.run(main())
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Streaming surfaces
|
|
40
|
+
|
|
41
|
+
- `session.send(text)` — yields the turn's events; client-executed tools
|
|
42
|
+
you registered (`tools=` on `sessions.create` / `session.on_tool_call`)
|
|
43
|
+
are run and their results returned to the turn automatically. Handlers
|
|
44
|
+
may be sync or async.
|
|
45
|
+
- `session.send_raw(text)` — same events, **no auto-dispatch**: hosts
|
|
46
|
+
that own their tool dispatcher consume this.
|
|
47
|
+
- `session.stream(after)` — replay the journal after a seq, then
|
|
48
|
+
live-tail; survives restarts.
|
|
49
|
+
- `session.resume(after)` — reconnect to the current turn only.
|
|
50
|
+
|
|
51
|
+
Every event is a `SessionEvent` dataclass (`TextDeltaEvent`,
|
|
52
|
+
`ToolCallEvent`, `TurnEndEvent`, …); a type newer than this SDK arrives as
|
|
53
|
+
`UnknownEvent` with the payload in `raw`. Non-2xx answers raise
|
|
54
|
+
`TarsApiError` with `status` and the stable `code`.
|
|
55
|
+
|
|
56
|
+
## Everything else
|
|
57
|
+
|
|
58
|
+
`tars.skills`, `tars.mcp_credentials`, `tars.mcp.list_tools`, `tars.usage`,
|
|
59
|
+
`tars.api_keys`, `tars.completions` and `tars.admin` (admin keys) cover the
|
|
60
|
+
rest of the API, one typed method per `/v1/*` route. On
|
|
61
|
+
`agents.update`, a keyword you do not pass is unchanged; passing `None` for
|
|
62
|
+
a clearable field (description, tools, metadata, mcp_servers, skills,
|
|
63
|
+
environment, reasoning) clears it.
|
|
64
|
+
|
|
65
|
+
Full docs: the TARS docs site, *SDK for Python* page.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tars-sdk"
|
|
7
|
+
# Bumped by hand in the PR that adds the CHANGELOG entry; Verify refuses the
|
|
8
|
+
# bump without the `release-approved` label; merging publishes to PyPI.
|
|
9
|
+
version = "0.1.1"
|
|
10
|
+
description = "Client SDK for TARS — the self-hosted managed-agents platform (agents, durable sessions, streaming turns, client-executed tools, files)."
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
authors = [{ name = "Legendari" }]
|
|
15
|
+
keywords = ["agents", "ai", "mcp", "anthropic", "sdk", "tars"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
# httpx is the one dependency: async HTTP with streaming responses, the
|
|
28
|
+
# transport seam tests use (MockTransport), and HTTP/2-ready.
|
|
29
|
+
dependencies = ["httpx>=0.27,<1"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/LegendariTech/tars"
|
|
33
|
+
Repository = "https://github.com/LegendariTech/tars"
|
|
34
|
+
Changelog = "https://github.com/LegendariTech/tars/blob/dev/sdk-python/CHANGELOG.md"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = ["pytest>=8", "pytest-asyncio>=0.24"]
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["src/tars_sdk"]
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.sdist]
|
|
43
|
+
include = ["src/tars_sdk", "README.md", "CHANGELOG.md", "pyproject.toml"]
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
asyncio_mode = "auto"
|
|
47
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
48
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""tars-sdk — the Python client for TARS.
|
|
2
|
+
|
|
3
|
+
from tars_sdk import TarsClient, TextDeltaEvent
|
|
4
|
+
|
|
5
|
+
async with TarsClient(api_key="tars_sk_…", base_url="https://your-tars-host") as tars:
|
|
6
|
+
agent = await tars.agents.create(name="assistant", system="You are helpful.")
|
|
7
|
+
session = await tars.sessions.create(agent_id=agent.id)
|
|
8
|
+
async for ev in session.send("Hello!"):
|
|
9
|
+
if isinstance(ev, TextDeltaEvent):
|
|
10
|
+
print(ev.text, end="")
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from importlib.metadata import PackageNotFoundError, version as _version
|
|
14
|
+
|
|
15
|
+
from ._wire import UNSET
|
|
16
|
+
from .client import (
|
|
17
|
+
DEFAULT_BASE_URL,
|
|
18
|
+
KEYED_PROVIDERS,
|
|
19
|
+
AdminClient,
|
|
20
|
+
AgentsClient,
|
|
21
|
+
ApiKeysClient,
|
|
22
|
+
CompletionsClient,
|
|
23
|
+
FilesClient,
|
|
24
|
+
McpClient,
|
|
25
|
+
McpCredentialsClient,
|
|
26
|
+
SessionsClient,
|
|
27
|
+
SkillsClient,
|
|
28
|
+
TarsClient,
|
|
29
|
+
UsageClient,
|
|
30
|
+
)
|
|
31
|
+
from .errors import TarsApiError
|
|
32
|
+
from .events import (
|
|
33
|
+
AGENT_ERROR_CODES,
|
|
34
|
+
SESSION_EVENT_TYPES,
|
|
35
|
+
CompactionEvent,
|
|
36
|
+
ConnectRequiredEvent,
|
|
37
|
+
ErrorEvent,
|
|
38
|
+
McpConnectEvent,
|
|
39
|
+
MessageAttachment,
|
|
40
|
+
OutputFileEvent,
|
|
41
|
+
SessionEvent,
|
|
42
|
+
StatusEvent,
|
|
43
|
+
TextDeltaEvent,
|
|
44
|
+
TextEndEvent,
|
|
45
|
+
TextStartEvent,
|
|
46
|
+
ToolCallEvent,
|
|
47
|
+
ToolResultEvent,
|
|
48
|
+
TurnEndEvent,
|
|
49
|
+
TurnStartEvent,
|
|
50
|
+
TurnUsage,
|
|
51
|
+
UnknownEvent,
|
|
52
|
+
UserMessageEvent,
|
|
53
|
+
parse_event,
|
|
54
|
+
)
|
|
55
|
+
from .models import (
|
|
56
|
+
Agent,
|
|
57
|
+
AgentEnvironment,
|
|
58
|
+
AgentPackages,
|
|
59
|
+
AgentStatusResult,
|
|
60
|
+
AgentVersion,
|
|
61
|
+
ApiKeyInfo,
|
|
62
|
+
AttachResult,
|
|
63
|
+
BearerCredential,
|
|
64
|
+
BuiltinToolInfo,
|
|
65
|
+
ClientIdentity,
|
|
66
|
+
ClientTool,
|
|
67
|
+
ClientToolDeclaration,
|
|
68
|
+
CompactResult,
|
|
69
|
+
CompletionResult,
|
|
70
|
+
CompletionUsage,
|
|
71
|
+
CreatedApiKey,
|
|
72
|
+
CreatedTenant,
|
|
73
|
+
DailyUsageRow,
|
|
74
|
+
FileMeta,
|
|
75
|
+
InterruptResult,
|
|
76
|
+
McpCredential,
|
|
77
|
+
McpCredentialStatus,
|
|
78
|
+
McpServer,
|
|
79
|
+
McpServerTools,
|
|
80
|
+
McpToolAnnotations,
|
|
81
|
+
McpToolInfo,
|
|
82
|
+
ModelInfo,
|
|
83
|
+
ModelPricing,
|
|
84
|
+
MountResult,
|
|
85
|
+
OAuthCredential,
|
|
86
|
+
OkResult,
|
|
87
|
+
PlatformStats,
|
|
88
|
+
PlatformUsageTotals,
|
|
89
|
+
ProviderStatus,
|
|
90
|
+
ProviderStatusReport,
|
|
91
|
+
ReasoningConfig,
|
|
92
|
+
SessionClientTool,
|
|
93
|
+
SessionEventsPage,
|
|
94
|
+
SessionInfo,
|
|
95
|
+
SessionListItem,
|
|
96
|
+
SessionsPage,
|
|
97
|
+
SessionToolSurface,
|
|
98
|
+
SessionUsage,
|
|
99
|
+
SessionUsageFigures,
|
|
100
|
+
SessionUsageRow,
|
|
101
|
+
Skill,
|
|
102
|
+
SkillPin,
|
|
103
|
+
SkillVersion,
|
|
104
|
+
SkillVersionPushed,
|
|
105
|
+
Tenant,
|
|
106
|
+
TenantCounts,
|
|
107
|
+
TenantDetail,
|
|
108
|
+
TenantFirstKey,
|
|
109
|
+
TenantStats,
|
|
110
|
+
ToolHandler,
|
|
111
|
+
UsageBreakdownRow,
|
|
112
|
+
UsageSummary,
|
|
113
|
+
UsageTotals,
|
|
114
|
+
)
|
|
115
|
+
from .session import TarsSession
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
__version__ = _version("tars-sdk")
|
|
119
|
+
except PackageNotFoundError: # running from a source checkout
|
|
120
|
+
__version__ = "0.0.0"
|
|
121
|
+
|
|
122
|
+
__all__ = [
|
|
123
|
+
"AGENT_ERROR_CODES",
|
|
124
|
+
"DEFAULT_BASE_URL",
|
|
125
|
+
"KEYED_PROVIDERS",
|
|
126
|
+
"SESSION_EVENT_TYPES",
|
|
127
|
+
"UNSET",
|
|
128
|
+
"AdminClient",
|
|
129
|
+
"Agent",
|
|
130
|
+
"AgentEnvironment",
|
|
131
|
+
"AgentPackages",
|
|
132
|
+
"AgentStatusResult",
|
|
133
|
+
"AgentVersion",
|
|
134
|
+
"AgentsClient",
|
|
135
|
+
"ApiKeyInfo",
|
|
136
|
+
"ApiKeysClient",
|
|
137
|
+
"AttachResult",
|
|
138
|
+
"BearerCredential",
|
|
139
|
+
"BuiltinToolInfo",
|
|
140
|
+
"ClientIdentity",
|
|
141
|
+
"ClientTool",
|
|
142
|
+
"ClientToolDeclaration",
|
|
143
|
+
"CompactResult",
|
|
144
|
+
"CompactionEvent",
|
|
145
|
+
"CompletionResult",
|
|
146
|
+
"CompletionUsage",
|
|
147
|
+
"CompletionsClient",
|
|
148
|
+
"ConnectRequiredEvent",
|
|
149
|
+
"CreatedApiKey",
|
|
150
|
+
"CreatedTenant",
|
|
151
|
+
"DailyUsageRow",
|
|
152
|
+
"ErrorEvent",
|
|
153
|
+
"FileMeta",
|
|
154
|
+
"FilesClient",
|
|
155
|
+
"InterruptResult",
|
|
156
|
+
"McpClient",
|
|
157
|
+
"McpConnectEvent",
|
|
158
|
+
"McpCredential",
|
|
159
|
+
"McpCredentialStatus",
|
|
160
|
+
"McpCredentialsClient",
|
|
161
|
+
"McpServer",
|
|
162
|
+
"McpServerTools",
|
|
163
|
+
"McpToolAnnotations",
|
|
164
|
+
"McpToolInfo",
|
|
165
|
+
"MessageAttachment",
|
|
166
|
+
"ModelInfo",
|
|
167
|
+
"ModelPricing",
|
|
168
|
+
"MountResult",
|
|
169
|
+
"OAuthCredential",
|
|
170
|
+
"OkResult",
|
|
171
|
+
"OutputFileEvent",
|
|
172
|
+
"PlatformStats",
|
|
173
|
+
"PlatformUsageTotals",
|
|
174
|
+
"ProviderStatus",
|
|
175
|
+
"ProviderStatusReport",
|
|
176
|
+
"ReasoningConfig",
|
|
177
|
+
"SessionClientTool",
|
|
178
|
+
"SessionEvent",
|
|
179
|
+
"SessionEventsPage",
|
|
180
|
+
"SessionInfo",
|
|
181
|
+
"SessionListItem",
|
|
182
|
+
"SessionToolSurface",
|
|
183
|
+
"SessionUsage",
|
|
184
|
+
"SessionUsageFigures",
|
|
185
|
+
"SessionUsageRow",
|
|
186
|
+
"SessionsClient",
|
|
187
|
+
"SessionsPage",
|
|
188
|
+
"Skill",
|
|
189
|
+
"SkillPin",
|
|
190
|
+
"SkillVersion",
|
|
191
|
+
"SkillVersionPushed",
|
|
192
|
+
"SkillsClient",
|
|
193
|
+
"StatusEvent",
|
|
194
|
+
"TarsApiError",
|
|
195
|
+
"TarsClient",
|
|
196
|
+
"TarsSession",
|
|
197
|
+
"Tenant",
|
|
198
|
+
"TenantCounts",
|
|
199
|
+
"TenantDetail",
|
|
200
|
+
"TenantFirstKey",
|
|
201
|
+
"TenantStats",
|
|
202
|
+
"TextDeltaEvent",
|
|
203
|
+
"TextEndEvent",
|
|
204
|
+
"TextStartEvent",
|
|
205
|
+
"ToolCallEvent",
|
|
206
|
+
"ToolHandler",
|
|
207
|
+
"ToolResultEvent",
|
|
208
|
+
"TurnEndEvent",
|
|
209
|
+
"TurnStartEvent",
|
|
210
|
+
"TurnUsage",
|
|
211
|
+
"UnknownEvent",
|
|
212
|
+
"UsageBreakdownRow",
|
|
213
|
+
"UsageClient",
|
|
214
|
+
"UsageSummary",
|
|
215
|
+
"UsageTotals",
|
|
216
|
+
"UserMessageEvent",
|
|
217
|
+
"__version__",
|
|
218
|
+
"parse_event",
|
|
219
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Minimal SSE reader for the session streams.
|
|
2
|
+
|
|
3
|
+
TARS writes ``id: <seq>\\ndata: <json>\\n\\n`` per event (``backend/src/http/sse.ts``);
|
|
4
|
+
the seq is also inside the JSON, so ``id:`` is ignored. ``data:`` lines of one
|
|
5
|
+
frame are joined, a blank line ends the frame, EOF flushes a frame the server
|
|
6
|
+
closed without a blank line.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
from collections.abc import AsyncIterator
|
|
13
|
+
|
|
14
|
+
import httpx
|
|
15
|
+
|
|
16
|
+
from .events import SessionEvent, parse_event
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def read_events(response: httpx.Response) -> AsyncIterator[SessionEvent]:
|
|
20
|
+
data: list[str] = []
|
|
21
|
+
async for line in response.aiter_lines():
|
|
22
|
+
if line == "":
|
|
23
|
+
if data:
|
|
24
|
+
yield parse_event(json.loads("".join(data)))
|
|
25
|
+
data = []
|
|
26
|
+
continue
|
|
27
|
+
if line.startswith("data: "):
|
|
28
|
+
data.append(line[6:])
|
|
29
|
+
elif line.startswith("data:"):
|
|
30
|
+
data.append(line[5:])
|
|
31
|
+
# id:, event:, retry:, comments — not used by this stream.
|
|
32
|
+
if data:
|
|
33
|
+
yield parse_event(json.loads("".join(data)))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Request-body helpers.
|
|
2
|
+
|
|
3
|
+
``UNSET`` lets a method tell "the caller did not pass this" from "the caller
|
|
4
|
+
passed ``None``": for a PATCH that is "leave it" versus "clear it". ``body()``
|
|
5
|
+
drops the SDK's own unset/None fields; the caller's data inside them (tool
|
|
6
|
+
outputs, input schemas, metadata) is passed through verbatim.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Final
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _Unset:
|
|
15
|
+
__slots__ = ()
|
|
16
|
+
|
|
17
|
+
def __repr__(self) -> str:
|
|
18
|
+
return "UNSET"
|
|
19
|
+
|
|
20
|
+
def __bool__(self) -> bool:
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
UNSET: Final = _Unset()
|
|
25
|
+
"""Default for optional update fields: not sent. Pass ``None`` to clear a field."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def body(**fields: Any) -> dict[str, Any]:
|
|
29
|
+
"""The SDK's own top-level fields, minus the ones left ``None``/``UNSET``."""
|
|
30
|
+
return {k: v for k, v in fields.items() if v is not None and v is not UNSET}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def patch(**fields: Any) -> dict[str, Any]:
|
|
34
|
+
"""Like :func:`body`, but a ``None`` is kept (sent as JSON ``null``); only ``UNSET`` is dropped."""
|
|
35
|
+
return {k: v for k, v in fields.items() if v is not UNSET}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def iso(at: Any) -> str:
|
|
39
|
+
"""UTC ISO 8601 with milliseconds and ``Z``, the shape the API expects."""
|
|
40
|
+
from datetime import datetime, timezone
|
|
41
|
+
|
|
42
|
+
if isinstance(at, str):
|
|
43
|
+
return at
|
|
44
|
+
if not isinstance(at, datetime):
|
|
45
|
+
raise TypeError(f"expected a datetime or ISO string, got {type(at).__name__}")
|
|
46
|
+
if at.tzinfo is None:
|
|
47
|
+
at = at.replace(tzinfo=timezone.utc)
|
|
48
|
+
at = at.astimezone(timezone.utc)
|
|
49
|
+
return at.strftime("%Y-%m-%dT%H:%M:%S.") + f"{at.microsecond // 1000:03d}Z"
|