@@ -31,10 +31,7 @@ for event in client.chat.stream("Summarize sales by region"):
3131 print (event[" text" ], end = " " , flush = True )
3232
3333# Upload files with a question
34- response = client.chat.create(
35- " Analyze this data" ,
36- files = [" ./sales.csv" ],
37- )
34+ response = client.chat.create(" Analyze this data" , files = [" ./sales.csv" ])
3835```
3936
4037## Resources
@@ -43,43 +40,101 @@ response = client.chat.create(
4340
4441``` python
4542client.chat.list(limit = 10 )
46- client.chat.create(" What connectors are available?" )
43+ client.chat.create(" What connectors are available?" , connector_ids = [1 ])
44+ client.chat.create(" Analyze this" , files = [" ./data.csv" ]) # multipart upload
45+ client.chat.stream(" Summarize revenue" ) # returns a Stream iterator
4746client.chat.get(" chat-uuid" )
48- client.chat.stream(" Summarize revenue" )
4947client.chat.cancel(" chat-uuid" )
5048```
5149
5250### Connectors
5351
52+ ` config ` is the connector configuration as documented by ` types() ` — ` connector_type `
53+ plus a per-type block (e.g. ` postgres ` , ` kdb ` , ` snowflake ` ).
54+
5455``` python
5556client.connectors.list()
57+ client.connectors.types() # every connector type and its fields (self-describing)
58+
59+ cfg = {
60+ " connector_type" : " POSTGRES" ,
61+ " name" : " prod-db" ,
62+ " postgres" : {" host" : " db.example.com" , " port" : 5432 , " user" : " ro" , " password" : " ..." , " database" : " app" },
63+ }
64+ client.connectors.test(cfg) # {"success": bool, "error": str} — a failed connection is success=False, not an error
65+ created = client.connectors.create(cfg, allow_sql_write_operations = False )
66+ client.connectors.update(created[" connector_id" ], cfg)
67+ client.connectors.delete(created[" connector_id" ])
68+ ```
69+
70+ ### Models
71+
72+ ``` python
73+ client.models.list() # models a key may pass as the chat `model` field
5674```
5775
5876### Playbooks
5977
6078``` python
6179client.playbooks.list(limit = 10 )
6280pb = client.playbooks.create()
81+ client.playbooks.get(pb[" id" ])
6382client.playbooks.update(pb[" id" ], name = " Weekly Revenue" , prompt = " Summarize revenue by region" )
6483client.playbooks.deploy(pb[" id" ])
65- client.playbooks.run(pb[" id" ])
66- client.playbooks.get(pb[" id" ])
84+ client.playbooks.run(pb[" id" ]) # run(..., dry_run=True) to validate without sending
6785client.playbooks.delete(pb[" id" ])
6886```
6987
7088### Sandbox
7189
90+ A sandbox is a stateful Python/SQL workspace. Start one, run code or load connector
91+ data into DataFrames, manage its files, then stop it.
92+
7293``` python
73- sb = client.sandbox.start()
94+ sb = client.sandbox.start() # start(sandbox_id="...") restarts a specific one
7495sid = sb[" sandbox_id" ]
7596
97+ client.sandbox.list(status = " all" ) # also: limit=, cursor=
98+ client.sandbox.status(sid)
99+ client.sandbox.executions(sid) # recorded run history (limit=, cursor=)
100+
101+ # Run code / commands
76102client.sandbox.execute(sid, code = " import pandas as pd; print(pd.__version__)" )
103+ client.sandbox.exec(sid, command = " ls -la" ) # bash by default
104+ client.sandbox.exec(sid, command = " print('hi')" , kind = " python" )
105+
106+ # Load connector data into a DataFrame (exactly one of query / tql_path)
77107client.sandbox.query(sid, connector_id = 1 , query = " SELECT * FROM sales LIMIT 10" , dataframe_name = " sales" )
108+ client.sandbox.query(sid, connector_id = 2 , tql_path = " portfolio/current_positions.tql" , params = {" as_of" : " 2026-01-01" }, max_rows = 500 )
109+
110+ # Files
78111client.sandbox.upload_file(sid, " ./data.csv" )
79- client.sandbox.status(sid)
112+ client.sandbox.list_files(sid) # path="subdir" to scope
113+ content: bytes = client.sandbox.download_file(sid, " out/report.csv" )
114+ client.sandbox.delete_file(sid, " out/report.csv" )
115+
116+ # Library write-back
117+ client.sandbox.library_diff(sid)
118+ client.sandbox.create_library_patch(sid, title = " Add metric" , description = " ..." , draft = True )
119+
80120client.sandbox.stop(sid)
81121```
82122
123+ ## Errors
124+
125+ Non-2xx responses raise typed exceptions, all subclasses of ` textql.APIError ` :
126+
127+ ``` python
128+ from textql import TextQL, NotFoundError, RateLimitError, AuthenticationError
129+
130+ try :
131+ client.chat.get(" does-not-exist" )
132+ except NotFoundError as e:
133+ print (e.status_code, e.request_id)
134+ ```
135+
136+ Also exported: ` PermissionDeniedError ` , ` APITimeoutError ` , ` APIConnectionError ` .
137+
83138## Configuration
84139
85140| Option | Env var | Default |
@@ -88,7 +143,13 @@ client.sandbox.stop(sid)
88143| ` base_url ` | ` TEXTQL_BASE_URL ` | ` https://app.textql.com ` |
89144| ` timeout ` | — | ` 60.0 ` seconds |
90145
91- The ` base_url ` accepts a bare hostname (e.g. ` app.textql.com ` ) or a full URL.
146+ The ` base_url ` accepts a bare hostname (e.g. ` app.textql.com ` ) or a full URL. The
147+ client is a context manager:
148+
149+ ``` python
150+ with TextQL(api_key = " tql_..." ) as client:
151+ client.connectors.list()
152+ ```
92153
93154## Links
94155
0 commit comments