talqing 0.1.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.
- talqing-0.1.0/.gitignore +43 -0
- talqing-0.1.0/LICENSE +21 -0
- talqing-0.1.0/PKG-INFO +257 -0
- talqing-0.1.0/README.md +232 -0
- talqing-0.1.0/generate.py +947 -0
- talqing-0.1.0/pyproject.toml +55 -0
- talqing-0.1.0/src/talqing/__init__.py +34 -0
- talqing-0.1.0/src/talqing/_transport.py +361 -0
- talqing-0.1.0/src/talqing/_version.py +3 -0
- talqing-0.1.0/src/talqing/client.py +280 -0
- talqing-0.1.0/src/talqing/errors.py +32 -0
- talqing-0.1.0/src/talqing/gen/__init__.py +10 -0
- talqing-0.1.0/src/talqing/gen/api.py +3583 -0
- talqing-0.1.0/src/talqing/gen/async_api.py +3595 -0
- talqing-0.1.0/src/talqing/gen/types.py +4605 -0
- talqing-0.1.0/src/talqing/py.typed +0 -0
- talqing-0.1.0/tests/conftest.py +58 -0
- talqing-0.1.0/tests/test_readme.py +85 -0
- talqing-0.1.0/tests/test_requests.py +359 -0
- talqing-0.1.0/tests/test_surface.py +62 -0
talqing-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# secrets / env
|
|
2
|
+
.env
|
|
3
|
+
backend/.env
|
|
4
|
+
# Per-env app configs (secrets; not committed). example.config.yaml is the
|
|
5
|
+
# committed template and must stay free of real credentials.
|
|
6
|
+
backend/configs/*.config.yaml
|
|
7
|
+
!backend/configs/example.config.yaml
|
|
8
|
+
*.local
|
|
9
|
+
client_secret_*.json
|
|
10
|
+
|
|
11
|
+
# python
|
|
12
|
+
__pycache__/
|
|
13
|
+
*.py[cod]
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
*.egg-info/
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.mypy_cache/
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
|
|
21
|
+
# node / next
|
|
22
|
+
node_modules/
|
|
23
|
+
.next/
|
|
24
|
+
out/
|
|
25
|
+
clients/typescript/dist/
|
|
26
|
+
mcp/dist/
|
|
27
|
+
npm-debug.log*
|
|
28
|
+
.pnpm-debug.log*
|
|
29
|
+
*.tsbuildinfo
|
|
30
|
+
|
|
31
|
+
# data / volumes
|
|
32
|
+
pgdata*/
|
|
33
|
+
*.log
|
|
34
|
+
.tmp-*.json
|
|
35
|
+
|
|
36
|
+
# os / editor
|
|
37
|
+
.DS_Store
|
|
38
|
+
.idea/
|
|
39
|
+
.vscode/
|
|
40
|
+
|
|
41
|
+
# Monitoring inputs for create-secrets.sh — droplet IPs, the LiveKit metrics
|
|
42
|
+
# password and the Grafana OAuth client. Same reason as the config files.
|
|
43
|
+
deployment/prod/scripts/monitoring.env
|
talqing-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Talqing
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
talqing-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: talqing
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for the Talqing API
|
|
5
|
+
Project-URL: Homepage, https://talqing.com
|
|
6
|
+
Author: talqing
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: agents,ai,talqing,voice
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: httpx<1,>=0.27
|
|
20
|
+
Requires-Dist: typing-extensions>=4.12
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Talqing Python SDK
|
|
27
|
+
|
|
28
|
+
The Python client for the Talqing `/v1` API, generated from `openapi/openapi.json`.
|
|
29
|
+
It is the same set of operations, under the same names, as the TypeScript SDK —
|
|
30
|
+
both are generated from the surface `backend/api/sdk_surface.py` declares.
|
|
31
|
+
|
|
32
|
+
`httpx` is the only runtime dependency. Responses come back as decoded JSON,
|
|
33
|
+
described by TypedDicts: nothing is validated, coerced or renamed on arrival.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install talqing
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Python 3.10 or newer. The package ships a `py.typed` marker, so a type checker
|
|
42
|
+
in your project sees every TypedDict it declares.
|
|
43
|
+
|
|
44
|
+
To work against a checkout instead: `uv pip install -e clients/python`.
|
|
45
|
+
|
|
46
|
+
## Authentication
|
|
47
|
+
|
|
48
|
+
Create a personal access token on the dashboard's Tokens page. The client sends
|
|
49
|
+
it as `Authorization: Bearer <token>`.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from talqing import Talqing
|
|
53
|
+
|
|
54
|
+
with Talqing(token="tq_...", base_url="https://api.in.talqing.com") as talqing:
|
|
55
|
+
print(talqing.auth.me())
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`Talqing.from_env()` reads the same two values from `TALQING_API_KEY` and
|
|
59
|
+
`TALQING_BASE_URL`:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
export TALQING_API_KEY="your-personal-access-token"
|
|
63
|
+
export TALQING_BASE_URL="http://localhost:8000"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
with Talqing.from_env() as talqing:
|
|
68
|
+
print(talqing.auth.me())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`base_url` has no default. A client that quietly points at localhost fails in
|
|
72
|
+
production as a connection error three layers down; one that refuses to start
|
|
73
|
+
says what is actually wrong.
|
|
74
|
+
|
|
75
|
+
## The shape of it
|
|
76
|
+
|
|
77
|
+
Operations are reached by resource, exactly as the API names them:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
talqing.agents.list(limit=50)
|
|
81
|
+
talqing.agents.get(agent_id)
|
|
82
|
+
talqing.agents.versions.rollback(agent_id, 3)
|
|
83
|
+
talqing.calls.batches.pause(batch_id)
|
|
84
|
+
talqing.telephony.phone_numbers.assign(number_id, agent_id=agent_id)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Path parameters are positional; everything else — request body fields and query
|
|
88
|
+
parameters alike — is a keyword argument:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
agent = talqing.agents.create(
|
|
92
|
+
config={"name": "Support bot", "channel": "text", "prompt": "Be concise."}
|
|
93
|
+
)
|
|
94
|
+
talqing.agents.update(agent["id"], config={**agent["config"], "greeting": None})
|
|
95
|
+
talqing.agents.publish(agent["id"])
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**An argument you do not pass is not sent.** That is what makes a PATCH able to
|
|
99
|
+
say `null`:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
talqing.orgs.update(retention_days=None) # keep call content forever
|
|
103
|
+
talqing.orgs.update(name="Acme") # leave the retention policy alone
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The one name that is not the API's own is `telephony.phone_numbers.import_()` —
|
|
107
|
+
`import` is a Python keyword.
|
|
108
|
+
|
|
109
|
+
## Errors
|
|
110
|
+
|
|
111
|
+
Every non-2xx raises `TalqingAPIError`. There is one error shape, so there is
|
|
112
|
+
nothing to branch on:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from talqing import TalqingAPIError
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
talqing.agents.publish(agent_id)
|
|
119
|
+
except TalqingAPIError as error:
|
|
120
|
+
print(error.status_code, error) # 400 config is invalid
|
|
121
|
+
for problem in error.errors: # ['llm: unknown model gpt-4.9']
|
|
122
|
+
print(problem)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Pagination
|
|
126
|
+
|
|
127
|
+
Every list endpoint pages the same way, so one helper covers all of them.
|
|
128
|
+
Anything else the endpoint filters on passes straight through:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from talqing import paginate
|
|
132
|
+
|
|
133
|
+
for agent in paginate(talqing.agents.list):
|
|
134
|
+
print(agent["config"]["name"])
|
|
135
|
+
|
|
136
|
+
for call in paginate(talqing.calls.list, agent_id=agent_id, type="SIP_INBOUND"):
|
|
137
|
+
print(call["id"], call["cost"])
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Live streams
|
|
141
|
+
|
|
142
|
+
Six endpoints stay open and push events. Each frame is decoded and repeats its
|
|
143
|
+
own name in `event`, which is what tells the frames apart:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
for event in talqing.conversations.events(conversation_id):
|
|
147
|
+
if event["event"] == "assistant.delta":
|
|
148
|
+
print(event["text"], end="", flush=True)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Use it as a context manager when the loop may exit early, so the connection
|
|
152
|
+
closes with it:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
with talqing.knowledge.events(kb_id) as events:
|
|
156
|
+
for event in events:
|
|
157
|
+
if event["event"] == "status":
|
|
158
|
+
print("build finished:", event["status"])
|
|
159
|
+
break
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Text conversations
|
|
163
|
+
|
|
164
|
+
Text agents do not use LiveKit rooms. Open the conversation, then send turns —
|
|
165
|
+
both addressed by your own `contact_key`, so the same key always reaches the same
|
|
166
|
+
thread — and watch it happen on the stream above:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
conversation = talqing.conversations.create(contact_key="user-42", agent_id=agent_id)
|
|
170
|
+
|
|
171
|
+
talqing.conversations.messages.create(
|
|
172
|
+
contact_key="user-42", message="Hello", client_message_id=str(uuid4())
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
A message returns as soon as it is accepted, carrying the user's own item. The
|
|
177
|
+
agent's reply arrives on the event stream, or from
|
|
178
|
+
`talqing.conversations.items.list(conversation["id"])`.
|
|
179
|
+
|
|
180
|
+
Voice and video agents take a LiveKit room token instead:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
token = talqing.calls.token(agent_id=agent_id)
|
|
184
|
+
print(token["server_url"], token["participant_token"])
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Async
|
|
188
|
+
|
|
189
|
+
`AsyncTalqing` has the same surface with every operation a coroutine. A stream
|
|
190
|
+
is the exception: it is not awaited on either client, so `async for` reads the
|
|
191
|
+
way `for` does.
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
import asyncio
|
|
195
|
+
from talqing import AsyncTalqing, paginate_async
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
async def main() -> None:
|
|
199
|
+
async with AsyncTalqing.from_env() as talqing:
|
|
200
|
+
await talqing.agents.list()
|
|
201
|
+
|
|
202
|
+
async for agent in paginate_async(talqing.agents.list):
|
|
203
|
+
print(agent["id"])
|
|
204
|
+
|
|
205
|
+
async for event in talqing.copilot.agents.stream(agent_id):
|
|
206
|
+
print(event["event"])
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
asyncio.run(main())
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Types
|
|
213
|
+
|
|
214
|
+
Every request and response shape is exported from `talqing`, named as the API
|
|
215
|
+
names it:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
from talqing import AgentConfig, AgentResponse, CallOutcome, OperationRequest
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
A response is a plain `dict` at runtime — the TypedDicts describe it for your
|
|
222
|
+
type checker and your editor, and cost nothing when you run. That also means the
|
|
223
|
+
SDK can never reject a payload the API considers valid.
|
|
224
|
+
|
|
225
|
+
`Page[T]` is the shape every list endpoint returns:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
page = talqing.tools.list(limit=50)
|
|
229
|
+
page["items"], page["has_more"], page["limit"], page["offset"]
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Escape hatches
|
|
233
|
+
|
|
234
|
+
`talqing.request(...)` calls a path directly with this client's credentials and
|
|
235
|
+
error handling, for an endpoint that shipped since this SDK was generated.
|
|
236
|
+
`talqing.http` is the underlying `httpx.Client`, already carrying the base URL
|
|
237
|
+
and the token, for anything else.
|
|
238
|
+
|
|
239
|
+
`talqing.google_login_url()` and `talqing.oauth_start_url(provider)` build the
|
|
240
|
+
two browser redirects, which are not operations and so cannot be generated.
|
|
241
|
+
|
|
242
|
+
## Regenerating
|
|
243
|
+
|
|
244
|
+
`src/talqing/gen` is generated and checked in. After re-running
|
|
245
|
+
`openapi/export.py`:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
python clients/python/generate.py # rewrite it
|
|
249
|
+
python clients/python/generate.py --check # or just assert it is current
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Everything else in `src/talqing` is hand-written: the client, the transport, the
|
|
253
|
+
error, and the pagination helper — what the document cannot say.
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
cd clients/python && pytest && mypy
|
|
257
|
+
```
|
talqing-0.1.0/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# Talqing Python SDK
|
|
2
|
+
|
|
3
|
+
The Python client for the Talqing `/v1` API, generated from `openapi/openapi.json`.
|
|
4
|
+
It is the same set of operations, under the same names, as the TypeScript SDK —
|
|
5
|
+
both are generated from the surface `backend/api/sdk_surface.py` declares.
|
|
6
|
+
|
|
7
|
+
`httpx` is the only runtime dependency. Responses come back as decoded JSON,
|
|
8
|
+
described by TypedDicts: nothing is validated, coerced or renamed on arrival.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install talqing
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Python 3.10 or newer. The package ships a `py.typed` marker, so a type checker
|
|
17
|
+
in your project sees every TypedDict it declares.
|
|
18
|
+
|
|
19
|
+
To work against a checkout instead: `uv pip install -e clients/python`.
|
|
20
|
+
|
|
21
|
+
## Authentication
|
|
22
|
+
|
|
23
|
+
Create a personal access token on the dashboard's Tokens page. The client sends
|
|
24
|
+
it as `Authorization: Bearer <token>`.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from talqing import Talqing
|
|
28
|
+
|
|
29
|
+
with Talqing(token="tq_...", base_url="https://api.in.talqing.com") as talqing:
|
|
30
|
+
print(talqing.auth.me())
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`Talqing.from_env()` reads the same two values from `TALQING_API_KEY` and
|
|
34
|
+
`TALQING_BASE_URL`:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
export TALQING_API_KEY="your-personal-access-token"
|
|
38
|
+
export TALQING_BASE_URL="http://localhost:8000"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
with Talqing.from_env() as talqing:
|
|
43
|
+
print(talqing.auth.me())
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`base_url` has no default. A client that quietly points at localhost fails in
|
|
47
|
+
production as a connection error three layers down; one that refuses to start
|
|
48
|
+
says what is actually wrong.
|
|
49
|
+
|
|
50
|
+
## The shape of it
|
|
51
|
+
|
|
52
|
+
Operations are reached by resource, exactly as the API names them:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
talqing.agents.list(limit=50)
|
|
56
|
+
talqing.agents.get(agent_id)
|
|
57
|
+
talqing.agents.versions.rollback(agent_id, 3)
|
|
58
|
+
talqing.calls.batches.pause(batch_id)
|
|
59
|
+
talqing.telephony.phone_numbers.assign(number_id, agent_id=agent_id)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Path parameters are positional; everything else — request body fields and query
|
|
63
|
+
parameters alike — is a keyword argument:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
agent = talqing.agents.create(
|
|
67
|
+
config={"name": "Support bot", "channel": "text", "prompt": "Be concise."}
|
|
68
|
+
)
|
|
69
|
+
talqing.agents.update(agent["id"], config={**agent["config"], "greeting": None})
|
|
70
|
+
talqing.agents.publish(agent["id"])
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**An argument you do not pass is not sent.** That is what makes a PATCH able to
|
|
74
|
+
say `null`:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
talqing.orgs.update(retention_days=None) # keep call content forever
|
|
78
|
+
talqing.orgs.update(name="Acme") # leave the retention policy alone
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The one name that is not the API's own is `telephony.phone_numbers.import_()` —
|
|
82
|
+
`import` is a Python keyword.
|
|
83
|
+
|
|
84
|
+
## Errors
|
|
85
|
+
|
|
86
|
+
Every non-2xx raises `TalqingAPIError`. There is one error shape, so there is
|
|
87
|
+
nothing to branch on:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from talqing import TalqingAPIError
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
talqing.agents.publish(agent_id)
|
|
94
|
+
except TalqingAPIError as error:
|
|
95
|
+
print(error.status_code, error) # 400 config is invalid
|
|
96
|
+
for problem in error.errors: # ['llm: unknown model gpt-4.9']
|
|
97
|
+
print(problem)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Pagination
|
|
101
|
+
|
|
102
|
+
Every list endpoint pages the same way, so one helper covers all of them.
|
|
103
|
+
Anything else the endpoint filters on passes straight through:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from talqing import paginate
|
|
107
|
+
|
|
108
|
+
for agent in paginate(talqing.agents.list):
|
|
109
|
+
print(agent["config"]["name"])
|
|
110
|
+
|
|
111
|
+
for call in paginate(talqing.calls.list, agent_id=agent_id, type="SIP_INBOUND"):
|
|
112
|
+
print(call["id"], call["cost"])
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Live streams
|
|
116
|
+
|
|
117
|
+
Six endpoints stay open and push events. Each frame is decoded and repeats its
|
|
118
|
+
own name in `event`, which is what tells the frames apart:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
for event in talqing.conversations.events(conversation_id):
|
|
122
|
+
if event["event"] == "assistant.delta":
|
|
123
|
+
print(event["text"], end="", flush=True)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Use it as a context manager when the loop may exit early, so the connection
|
|
127
|
+
closes with it:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
with talqing.knowledge.events(kb_id) as events:
|
|
131
|
+
for event in events:
|
|
132
|
+
if event["event"] == "status":
|
|
133
|
+
print("build finished:", event["status"])
|
|
134
|
+
break
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Text conversations
|
|
138
|
+
|
|
139
|
+
Text agents do not use LiveKit rooms. Open the conversation, then send turns —
|
|
140
|
+
both addressed by your own `contact_key`, so the same key always reaches the same
|
|
141
|
+
thread — and watch it happen on the stream above:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
conversation = talqing.conversations.create(contact_key="user-42", agent_id=agent_id)
|
|
145
|
+
|
|
146
|
+
talqing.conversations.messages.create(
|
|
147
|
+
contact_key="user-42", message="Hello", client_message_id=str(uuid4())
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
A message returns as soon as it is accepted, carrying the user's own item. The
|
|
152
|
+
agent's reply arrives on the event stream, or from
|
|
153
|
+
`talqing.conversations.items.list(conversation["id"])`.
|
|
154
|
+
|
|
155
|
+
Voice and video agents take a LiveKit room token instead:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
token = talqing.calls.token(agent_id=agent_id)
|
|
159
|
+
print(token["server_url"], token["participant_token"])
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Async
|
|
163
|
+
|
|
164
|
+
`AsyncTalqing` has the same surface with every operation a coroutine. A stream
|
|
165
|
+
is the exception: it is not awaited on either client, so `async for` reads the
|
|
166
|
+
way `for` does.
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
import asyncio
|
|
170
|
+
from talqing import AsyncTalqing, paginate_async
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def main() -> None:
|
|
174
|
+
async with AsyncTalqing.from_env() as talqing:
|
|
175
|
+
await talqing.agents.list()
|
|
176
|
+
|
|
177
|
+
async for agent in paginate_async(talqing.agents.list):
|
|
178
|
+
print(agent["id"])
|
|
179
|
+
|
|
180
|
+
async for event in talqing.copilot.agents.stream(agent_id):
|
|
181
|
+
print(event["event"])
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
asyncio.run(main())
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Types
|
|
188
|
+
|
|
189
|
+
Every request and response shape is exported from `talqing`, named as the API
|
|
190
|
+
names it:
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from talqing import AgentConfig, AgentResponse, CallOutcome, OperationRequest
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
A response is a plain `dict` at runtime — the TypedDicts describe it for your
|
|
197
|
+
type checker and your editor, and cost nothing when you run. That also means the
|
|
198
|
+
SDK can never reject a payload the API considers valid.
|
|
199
|
+
|
|
200
|
+
`Page[T]` is the shape every list endpoint returns:
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
page = talqing.tools.list(limit=50)
|
|
204
|
+
page["items"], page["has_more"], page["limit"], page["offset"]
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Escape hatches
|
|
208
|
+
|
|
209
|
+
`talqing.request(...)` calls a path directly with this client's credentials and
|
|
210
|
+
error handling, for an endpoint that shipped since this SDK was generated.
|
|
211
|
+
`talqing.http` is the underlying `httpx.Client`, already carrying the base URL
|
|
212
|
+
and the token, for anything else.
|
|
213
|
+
|
|
214
|
+
`talqing.google_login_url()` and `talqing.oauth_start_url(provider)` build the
|
|
215
|
+
two browser redirects, which are not operations and so cannot be generated.
|
|
216
|
+
|
|
217
|
+
## Regenerating
|
|
218
|
+
|
|
219
|
+
`src/talqing/gen` is generated and checked in. After re-running
|
|
220
|
+
`openapi/export.py`:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
python clients/python/generate.py # rewrite it
|
|
224
|
+
python clients/python/generate.py --check # or just assert it is current
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Everything else in `src/talqing` is hand-written: the client, the transport, the
|
|
228
|
+
error, and the pagination helper — what the document cannot say.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
cd clients/python && pytest && mypy
|
|
232
|
+
```
|