tessaract 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.
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jennifer Umoke
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,277 @@
1
+ Metadata-Version: 2.4
2
+ Name: tessaract
3
+ Version: 0.1.0
4
+ Summary: Provider-agnostic SDK for building AI agents natively
5
+ Author: Jennifer Umoke
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Dist: pydantic>=2.10,<3
9
+ Requires-Dist: openai>=2.45.0 ; extra == 'openai'
10
+ Requires-Python: >=3.11
11
+ Project-URL: Repository, https://github.com/Chinenyay/tessaract
12
+ Project-URL: Issues, https://github.com/Chinenyay/tessaract/issues
13
+ Project-URL: Documentation, https://github.com/Chinenyay/tessaract/blob/main/README.md
14
+ Provides-Extra: openai
15
+ Description-Content-Type: text/markdown
16
+
17
+ # Tessaract
18
+
19
+ A provider-agnostic SDK for building AI agents natively.
20
+
21
+ Tessaract exists to make it straightforward to build agents that work across multiple providers. It is an unopinionated portal to multi-model intelligence giving you model diversity and full control over how you compose models in your applications.
22
+
23
+ Today, Tessaract supports OpenAI's Responses API, including streaming, reasoning, and tool calling. More provider adapters are planned.
24
+
25
+ More concretely, Tessaract exposes a **canonical model of the LLM ecosystem**: requests, responses, content, streaming events, tools, usage, errors and reasoning. You write an agent loop once against Tessaract's types, and a provider **adapter** translates to and from each vendor's native API.
26
+
27
+
28
+ > **Status:** OpenAI is the only supported provider today. Anthropic support is in progress.
29
+
30
+ ---
31
+
32
+ ## Contents
33
+
34
+ - [Features](#features)
35
+ - [Installation](#installation)
36
+ - [Quickstart](#quickstart)
37
+ - [Build an agent with reasoning and tool calling](#build-an-agent-with-reasoning-and-tool-calling)
38
+ - [Streaming](#streaming)
39
+ - [Documentation](#documentation)
40
+ - [Project layout](#project-layout)
41
+ - [Status and roadmap](#status-and-roadmap)
42
+
43
+ ---
44
+
45
+ ## Features
46
+
47
+ - **Provider prefixes.** Register a provider under a prefix and address models as `"<prefix>/<model>"`, e.g. `"oai/gpt-5.6-luna"`.
48
+ - **Canonical tools.** Define function tools once with `FunctionTool` / `InputSchema` / `Property`, including nested objects, arrays, enums and nullable fields. The adapter emits the provider's native schema.
49
+ - **Canonical reasoning.** Control effort and summaries with `ReasoningOptions`; read reasoning back as `ReasoningOutputItem`s.
50
+ - **Typed output.** Responses contain `AssistantMessage`, `ReasoningOutputItem`, `FunctionCallOutputItem` and `ProviderOutputItem` objects. Function-call arguments are already parsed into a `dict`.
51
+ - **Canonical streaming events.** Text, reasoning and tool-argument deltas are normalized into typed events (`text.delta`, `reasoning_summary.delta`, `response.completed`, …).
52
+ - **Lossless multi-turn history.** Every output item keeps its provider-native `raw` payload, so you can append a response's output straight back into the conversation history.
53
+ - **Escape hatches everywhere.** `request_options` passes any native request parameter through, `FunctionTool.provider_options` adds native tool fields, and `raw_response` / `raw_event` expose the original SDK objects.
54
+
55
+ ---
56
+
57
+ ## Installation
58
+
59
+ Tessaract requires **Python 3.11+**. Install the `openai` extra to use the currently supported provider.
60
+
61
+ With pip:
62
+
63
+ ```bash
64
+ python -m pip install "tessaract[openai]"
65
+ ```
66
+
67
+ With uv:
68
+
69
+ ```bash
70
+ uv add "tessaract[openai]"
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Quickstart
76
+
77
+ Set your OpenAI API key:
78
+
79
+ ```bash
80
+ export OPENAI_API_KEY="your-api-key"
81
+ ```
82
+
83
+ Then send your first request:
84
+
85
+ ```python
86
+ from tessaract import OpenAIProvider, Tessaract
87
+
88
+ client = Tessaract(providers={"oai": OpenAIProvider()})
89
+ response = client.send(model="oai/gpt-5.6-luna", input="Say hello in five words.")
90
+
91
+ print(response.output_text)
92
+ ```
93
+
94
+ `OpenAIProvider` reads `OPENAI_API_KEY` from the environment. The `oai` prefix is your choice; Tessaract sends the model name after `/` to OpenAI. See the [getting started guide](https://github.com/Chinenyay/tessaract/blob/main/docs/getting-started.md) for configuration and multi-turn conversations.
95
+
96
+ ---
97
+
98
+ ## Build an agent with reasoning and tool calling
99
+
100
+ An agent is a loop:
101
+
102
+ 1. Send the conversation history, plus the tools the model may use.
103
+ 2. Append the model's output (reasoning, messages and function calls) to the history.
104
+ 3. If the model asked for function calls, run them and append a `FunctionToolResult` for each one.
105
+ 4. Repeat until the model replies without calling a tool.
106
+
107
+ ```python
108
+ import json
109
+ import os
110
+ from datetime import datetime, timezone
111
+
112
+ from tessaract import (
113
+ FunctionTool,
114
+ FunctionToolResult,
115
+ InputSchema,
116
+ OpenAIProvider,
117
+ Property,
118
+ ReasoningOptions,
119
+ Tessaract,
120
+ UserMessage,
121
+ )
122
+
123
+ # 1. Plain Python functions the agent can call
124
+ def get_time() -> str:
125
+ return datetime.now(timezone.utc).isoformat()
126
+
127
+ def get_weather(city: str) -> str:
128
+ temps = {"paris": "19C", "amsterdam": "20C"}
129
+ return json.dumps({"temperature": temps.get(city.lower(), "unknown")})
130
+
131
+ FUNCTIONS = {"get_time": get_time, "get_weather": get_weather}
132
+
133
+ # 2. Describe them to the model
134
+ tools = [
135
+ FunctionTool(
136
+ name="get_time",
137
+ description="Get the current UTC time as an ISO-8601 string.",
138
+ strict=False,
139
+ ),
140
+ FunctionTool(
141
+ name="get_weather",
142
+ description="Get the current temperature for a city.",
143
+ input_schema=InputSchema(
144
+ properties={
145
+ "city": Property(type="string", description="City name, e.g. Paris."),
146
+ },
147
+ required=["city"],
148
+ additionalProperties=False,
149
+ ),
150
+ ),
151
+ ]
152
+
153
+ client = Tessaract(
154
+ providers={"oai": OpenAIProvider(api_key=os.environ["OPENAI_API_KEY"])}
155
+ )
156
+
157
+ # 3. The agent loop
158
+ def run_agent(history: list, user_text: str) -> str:
159
+ history.append(UserMessage(content=user_text))
160
+
161
+ while True:
162
+ response = client.send(
163
+ model="oai/gpt-5.6-luna",
164
+ input=history,
165
+ tools=tools,
166
+ reasoning=ReasoningOptions(effort="medium", summary="auto"),
167
+ request_options={"instructions": "You are a concise, helpful assistant."},
168
+ )
169
+
170
+ # Keep reasoning, messages and calls in the history for the next turn
171
+ history.extend(response.output)
172
+
173
+ for item in response.output:
174
+ if item.type == "reasoning" and item.text:
175
+ print(f"[thinking] {item.text}")
176
+
177
+ calls = [item for item in response.output if item.type == "function_call"]
178
+ if not calls:
179
+ return response.output_text
180
+
181
+ for call in calls:
182
+ result = FUNCTIONS[call.name](**call.arguments) # arguments is already a dict
183
+ history.append(FunctionToolResult(call_id=call.call_id, result=result))
184
+
185
+
186
+ history: list = []
187
+ print(run_agent(history, "What's the weather in Paris, and what time is it?"))
188
+ ```
189
+
190
+ The step-by-step walkthrough is in the [building an agent guide](https://github.com/Chinenyay/tessaract/blob/main/docs/building-an-agent.md), with runnable versions in [`examples/`](https://github.com/Chinenyay/tessaract/tree/main/examples).
191
+
192
+ ---
193
+
194
+ ## Streaming
195
+
196
+ Pass `stream=True` to get an iterator of canonical events. The final event is `response.completed`, which carries the same `Response` object that a non-streaming call returns, so the agent loop doesn't change:
197
+
198
+ ```python
199
+ completed = None
200
+
201
+ for event in client.send(
202
+ model="oai/gpt-5.6-luna",
203
+ input=history,
204
+ tools=tools,
205
+ stream=True,
206
+ reasoning=ReasoningOptions(effort="medium", summary="auto"),
207
+ ):
208
+ if event.type == "reasoning.started":
209
+ print("\n[thinking] ", end="")
210
+ elif event.type == "reasoning_summary.delta":
211
+ print(event.delta, end="", flush=True)
212
+ elif event.type == "text.delta":
213
+ print(event.delta, end="", flush=True)
214
+ elif event.type == "response.completed":
215
+ completed = event.response
216
+
217
+ history.extend(completed.output)
218
+ ```
219
+
220
+ See the [streaming guide](https://github.com/Chinenyay/tessaract/blob/main/docs/streaming.md) for the full event list.
221
+
222
+ ---
223
+
224
+ ## Documentation
225
+
226
+ | Guide | What it covers |
227
+ | --- | --- |
228
+ | [Getting started](https://github.com/Chinenyay/tessaract/blob/main/docs/getting-started.md) | Installing, configuring providers, sending your first request, and building multi-turn history |
229
+ | [Building an agent](https://github.com/Chinenyay/tessaract/blob/main/docs/building-an-agent.md) | A step-by-step tutorial for an agent with reasoning and tool calling, both synchronous and streaming |
230
+ | [Tool calling](https://github.com/Chinenyay/tessaract/blob/main/docs/tool-calling.md) | `FunctionTool`, `InputSchema`, `Property`, strict mode, tool results and `provider_options` |
231
+ | [Reasoning](https://github.com/Chinenyay/tessaract/blob/main/docs/reasoning.md) | `ReasoningOptions`, effort levels, summaries, and reading and preserving reasoning items |
232
+ | [Streaming](https://github.com/Chinenyay/tessaract/blob/main/docs/streaming.md) | Every canonical stream event, and how to build a streaming agent loop |
233
+ | [API reference](https://github.com/Chinenyay/tessaract/blob/main/docs/api-reference.md) | Every public class, field and method |
234
+ | [Architecture](https://github.com/Chinenyay/tessaract/blob/main/docs/architecture.md) | Canonical types, adapters and providers, and how to add a new provider |
235
+
236
+ ---
237
+
238
+ ## Project layout
239
+
240
+ ```
241
+ src/tessaract/
242
+ ├── client.py # Tessaract client: routes requests to adapters
243
+ ├── providers/
244
+ │ ├── provider.py # Provider base dataclass
245
+ │ └── openai_provider.py # OpenAIProvider (wraps openai.OpenAI)
246
+ ├── adapters/
247
+ │ ├── adapter.py # Adapter base class + protocols
248
+ │ └── openai/openai_adapter.py # Canonical <-> OpenAI Responses API translation
249
+ ├── tools/function.py # FunctionTool, InputSchema, Property
250
+ └── types/
251
+ ├── input_types.py # UserMessage, FunctionToolResult
252
+ ├── output_types.py # AssistantMessage, ReasoningOutputItem, FunctionCallOutputItem, ...
253
+ ├── request.py # Request, ReasoningOptions
254
+ ├── response.py # Response, ResponseStatus, ResponseError
255
+ └── streaming/event_types.py # Canonical stream events
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Status and roadmap
261
+
262
+ **Working today (OpenAI):**
263
+
264
+ - Synchronous and streaming requests via the Responses API
265
+ - Multi-turn conversations
266
+ - Function tools with parallel calls
267
+ - Reasoning effort and summaries
268
+ - Pass-through request options
269
+
270
+ **Planned:**
271
+
272
+ - Anthropic provider and adapter
273
+ - Google GenAI provider and adapter
274
+ - Token usage and finish details on `Response`
275
+ - TestProvider for CI and testing without making live API calls
276
+ - built-in utils: agent loop helper, tool schema autowriter
277
+ - Token counting
@@ -0,0 +1,261 @@
1
+ # Tessaract
2
+
3
+ A provider-agnostic SDK for building AI agents natively.
4
+
5
+ Tessaract exists to make it straightforward to build agents that work across multiple providers. It is an unopinionated portal to multi-model intelligence giving you model diversity and full control over how you compose models in your applications.
6
+
7
+ Today, Tessaract supports OpenAI's Responses API, including streaming, reasoning, and tool calling. More provider adapters are planned.
8
+
9
+ More concretely, Tessaract exposes a **canonical model of the LLM ecosystem**: requests, responses, content, streaming events, tools, usage, errors and reasoning. You write an agent loop once against Tessaract's types, and a provider **adapter** translates to and from each vendor's native API.
10
+
11
+
12
+ > **Status:** OpenAI is the only supported provider today. Anthropic support is in progress.
13
+
14
+ ---
15
+
16
+ ## Contents
17
+
18
+ - [Features](#features)
19
+ - [Installation](#installation)
20
+ - [Quickstart](#quickstart)
21
+ - [Build an agent with reasoning and tool calling](#build-an-agent-with-reasoning-and-tool-calling)
22
+ - [Streaming](#streaming)
23
+ - [Documentation](#documentation)
24
+ - [Project layout](#project-layout)
25
+ - [Status and roadmap](#status-and-roadmap)
26
+
27
+ ---
28
+
29
+ ## Features
30
+
31
+ - **Provider prefixes.** Register a provider under a prefix and address models as `"<prefix>/<model>"`, e.g. `"oai/gpt-5.6-luna"`.
32
+ - **Canonical tools.** Define function tools once with `FunctionTool` / `InputSchema` / `Property`, including nested objects, arrays, enums and nullable fields. The adapter emits the provider's native schema.
33
+ - **Canonical reasoning.** Control effort and summaries with `ReasoningOptions`; read reasoning back as `ReasoningOutputItem`s.
34
+ - **Typed output.** Responses contain `AssistantMessage`, `ReasoningOutputItem`, `FunctionCallOutputItem` and `ProviderOutputItem` objects. Function-call arguments are already parsed into a `dict`.
35
+ - **Canonical streaming events.** Text, reasoning and tool-argument deltas are normalized into typed events (`text.delta`, `reasoning_summary.delta`, `response.completed`, …).
36
+ - **Lossless multi-turn history.** Every output item keeps its provider-native `raw` payload, so you can append a response's output straight back into the conversation history.
37
+ - **Escape hatches everywhere.** `request_options` passes any native request parameter through, `FunctionTool.provider_options` adds native tool fields, and `raw_response` / `raw_event` expose the original SDK objects.
38
+
39
+ ---
40
+
41
+ ## Installation
42
+
43
+ Tessaract requires **Python 3.11+**. Install the `openai` extra to use the currently supported provider.
44
+
45
+ With pip:
46
+
47
+ ```bash
48
+ python -m pip install "tessaract[openai]"
49
+ ```
50
+
51
+ With uv:
52
+
53
+ ```bash
54
+ uv add "tessaract[openai]"
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Quickstart
60
+
61
+ Set your OpenAI API key:
62
+
63
+ ```bash
64
+ export OPENAI_API_KEY="your-api-key"
65
+ ```
66
+
67
+ Then send your first request:
68
+
69
+ ```python
70
+ from tessaract import OpenAIProvider, Tessaract
71
+
72
+ client = Tessaract(providers={"oai": OpenAIProvider()})
73
+ response = client.send(model="oai/gpt-5.6-luna", input="Say hello in five words.")
74
+
75
+ print(response.output_text)
76
+ ```
77
+
78
+ `OpenAIProvider` reads `OPENAI_API_KEY` from the environment. The `oai` prefix is your choice; Tessaract sends the model name after `/` to OpenAI. See the [getting started guide](https://github.com/Chinenyay/tessaract/blob/main/docs/getting-started.md) for configuration and multi-turn conversations.
79
+
80
+ ---
81
+
82
+ ## Build an agent with reasoning and tool calling
83
+
84
+ An agent is a loop:
85
+
86
+ 1. Send the conversation history, plus the tools the model may use.
87
+ 2. Append the model's output (reasoning, messages and function calls) to the history.
88
+ 3. If the model asked for function calls, run them and append a `FunctionToolResult` for each one.
89
+ 4. Repeat until the model replies without calling a tool.
90
+
91
+ ```python
92
+ import json
93
+ import os
94
+ from datetime import datetime, timezone
95
+
96
+ from tessaract import (
97
+ FunctionTool,
98
+ FunctionToolResult,
99
+ InputSchema,
100
+ OpenAIProvider,
101
+ Property,
102
+ ReasoningOptions,
103
+ Tessaract,
104
+ UserMessage,
105
+ )
106
+
107
+ # 1. Plain Python functions the agent can call
108
+ def get_time() -> str:
109
+ return datetime.now(timezone.utc).isoformat()
110
+
111
+ def get_weather(city: str) -> str:
112
+ temps = {"paris": "19C", "amsterdam": "20C"}
113
+ return json.dumps({"temperature": temps.get(city.lower(), "unknown")})
114
+
115
+ FUNCTIONS = {"get_time": get_time, "get_weather": get_weather}
116
+
117
+ # 2. Describe them to the model
118
+ tools = [
119
+ FunctionTool(
120
+ name="get_time",
121
+ description="Get the current UTC time as an ISO-8601 string.",
122
+ strict=False,
123
+ ),
124
+ FunctionTool(
125
+ name="get_weather",
126
+ description="Get the current temperature for a city.",
127
+ input_schema=InputSchema(
128
+ properties={
129
+ "city": Property(type="string", description="City name, e.g. Paris."),
130
+ },
131
+ required=["city"],
132
+ additionalProperties=False,
133
+ ),
134
+ ),
135
+ ]
136
+
137
+ client = Tessaract(
138
+ providers={"oai": OpenAIProvider(api_key=os.environ["OPENAI_API_KEY"])}
139
+ )
140
+
141
+ # 3. The agent loop
142
+ def run_agent(history: list, user_text: str) -> str:
143
+ history.append(UserMessage(content=user_text))
144
+
145
+ while True:
146
+ response = client.send(
147
+ model="oai/gpt-5.6-luna",
148
+ input=history,
149
+ tools=tools,
150
+ reasoning=ReasoningOptions(effort="medium", summary="auto"),
151
+ request_options={"instructions": "You are a concise, helpful assistant."},
152
+ )
153
+
154
+ # Keep reasoning, messages and calls in the history for the next turn
155
+ history.extend(response.output)
156
+
157
+ for item in response.output:
158
+ if item.type == "reasoning" and item.text:
159
+ print(f"[thinking] {item.text}")
160
+
161
+ calls = [item for item in response.output if item.type == "function_call"]
162
+ if not calls:
163
+ return response.output_text
164
+
165
+ for call in calls:
166
+ result = FUNCTIONS[call.name](**call.arguments) # arguments is already a dict
167
+ history.append(FunctionToolResult(call_id=call.call_id, result=result))
168
+
169
+
170
+ history: list = []
171
+ print(run_agent(history, "What's the weather in Paris, and what time is it?"))
172
+ ```
173
+
174
+ The step-by-step walkthrough is in the [building an agent guide](https://github.com/Chinenyay/tessaract/blob/main/docs/building-an-agent.md), with runnable versions in [`examples/`](https://github.com/Chinenyay/tessaract/tree/main/examples).
175
+
176
+ ---
177
+
178
+ ## Streaming
179
+
180
+ Pass `stream=True` to get an iterator of canonical events. The final event is `response.completed`, which carries the same `Response` object that a non-streaming call returns, so the agent loop doesn't change:
181
+
182
+ ```python
183
+ completed = None
184
+
185
+ for event in client.send(
186
+ model="oai/gpt-5.6-luna",
187
+ input=history,
188
+ tools=tools,
189
+ stream=True,
190
+ reasoning=ReasoningOptions(effort="medium", summary="auto"),
191
+ ):
192
+ if event.type == "reasoning.started":
193
+ print("\n[thinking] ", end="")
194
+ elif event.type == "reasoning_summary.delta":
195
+ print(event.delta, end="", flush=True)
196
+ elif event.type == "text.delta":
197
+ print(event.delta, end="", flush=True)
198
+ elif event.type == "response.completed":
199
+ completed = event.response
200
+
201
+ history.extend(completed.output)
202
+ ```
203
+
204
+ See the [streaming guide](https://github.com/Chinenyay/tessaract/blob/main/docs/streaming.md) for the full event list.
205
+
206
+ ---
207
+
208
+ ## Documentation
209
+
210
+ | Guide | What it covers |
211
+ | --- | --- |
212
+ | [Getting started](https://github.com/Chinenyay/tessaract/blob/main/docs/getting-started.md) | Installing, configuring providers, sending your first request, and building multi-turn history |
213
+ | [Building an agent](https://github.com/Chinenyay/tessaract/blob/main/docs/building-an-agent.md) | A step-by-step tutorial for an agent with reasoning and tool calling, both synchronous and streaming |
214
+ | [Tool calling](https://github.com/Chinenyay/tessaract/blob/main/docs/tool-calling.md) | `FunctionTool`, `InputSchema`, `Property`, strict mode, tool results and `provider_options` |
215
+ | [Reasoning](https://github.com/Chinenyay/tessaract/blob/main/docs/reasoning.md) | `ReasoningOptions`, effort levels, summaries, and reading and preserving reasoning items |
216
+ | [Streaming](https://github.com/Chinenyay/tessaract/blob/main/docs/streaming.md) | Every canonical stream event, and how to build a streaming agent loop |
217
+ | [API reference](https://github.com/Chinenyay/tessaract/blob/main/docs/api-reference.md) | Every public class, field and method |
218
+ | [Architecture](https://github.com/Chinenyay/tessaract/blob/main/docs/architecture.md) | Canonical types, adapters and providers, and how to add a new provider |
219
+
220
+ ---
221
+
222
+ ## Project layout
223
+
224
+ ```
225
+ src/tessaract/
226
+ ├── client.py # Tessaract client: routes requests to adapters
227
+ ├── providers/
228
+ │ ├── provider.py # Provider base dataclass
229
+ │ └── openai_provider.py # OpenAIProvider (wraps openai.OpenAI)
230
+ ├── adapters/
231
+ │ ├── adapter.py # Adapter base class + protocols
232
+ │ └── openai/openai_adapter.py # Canonical <-> OpenAI Responses API translation
233
+ ├── tools/function.py # FunctionTool, InputSchema, Property
234
+ └── types/
235
+ ├── input_types.py # UserMessage, FunctionToolResult
236
+ ├── output_types.py # AssistantMessage, ReasoningOutputItem, FunctionCallOutputItem, ...
237
+ ├── request.py # Request, ReasoningOptions
238
+ ├── response.py # Response, ResponseStatus, ResponseError
239
+ └── streaming/event_types.py # Canonical stream events
240
+ ```
241
+
242
+ ---
243
+
244
+ ## Status and roadmap
245
+
246
+ **Working today (OpenAI):**
247
+
248
+ - Synchronous and streaming requests via the Responses API
249
+ - Multi-turn conversations
250
+ - Function tools with parallel calls
251
+ - Reasoning effort and summaries
252
+ - Pass-through request options
253
+
254
+ **Planned:**
255
+
256
+ - Anthropic provider and adapter
257
+ - Google GenAI provider and adapter
258
+ - Token usage and finish details on `Response`
259
+ - TestProvider for CI and testing without making live API calls
260
+ - built-in utils: agent loop helper, tool schema autowriter
261
+ - Token counting
@@ -0,0 +1,24 @@
1
+ [project]
2
+ name = "tessaract"
3
+ version = "0.1.0"
4
+ description = "Provider-agnostic SDK for building AI agents natively"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = ["pydantic>=2.10,<3"]
8
+ license = "MIT"
9
+ license-files = ["LICENSE"]
10
+
11
+ [[project.authors]]
12
+ name = "Jennifer Umoke"
13
+
14
+ [project.optional-dependencies]
15
+ openai = ["openai>=2.45.0"]
16
+
17
+ [project.urls]
18
+ Repository = "https://github.com/Chinenyay/tessaract"
19
+ Issues = "https://github.com/Chinenyay/tessaract/issues"
20
+ Documentation = "https://github.com/Chinenyay/tessaract/blob/main/README.md"
21
+
22
+ [build-system]
23
+ requires = ["uv_build>=0.12.13, <0.13"]
24
+ build-backend = "uv_build"
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "tessaract"
3
+ version = "0.1.0"
4
+ description = "Provider-agnostic SDK for building AI agents natively"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "pydantic>=2.10,<3"
9
+ ]
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [
13
+ { "name" = "Jennifer Umoke" },
14
+ ]
15
+ [project.optional-dependencies]
16
+ openai = [
17
+ "openai>=2.45.0",
18
+ ]
19
+
20
+
21
+ [project.urls]
22
+ Repository = "https://github.com/Chinenyay/tessaract"
23
+ Issues = "https://github.com/Chinenyay/tessaract/issues"
24
+ Documentation = "https://github.com/Chinenyay/tessaract/blob/main/README.md"
25
+
26
+ [build-system]
27
+ requires = ["uv_build>=0.12.13, <0.13"]
28
+ build-backend = "uv_build"
29
+
@@ -0,0 +1,16 @@
1
+ from .client import Tessaract
2
+ from .providers.openai_provider import OpenAIProvider
3
+ from .tools.function import FunctionTool, InputSchema, Property
4
+ from .types.input_types import FunctionToolResult, UserMessage
5
+ from .types.request import ReasoningOptions
6
+
7
+ __all__ = [
8
+ "FunctionTool",
9
+ "FunctionToolResult",
10
+ "InputSchema",
11
+ "OpenAIProvider",
12
+ "Property",
13
+ "ReasoningOptions",
14
+ "Tessaract",
15
+ "UserMessage"
16
+ ]
@@ -0,0 +1,3 @@
1
+ from .adapter import Adapter
2
+
3
+ __all__ = ["Adapter"]
@@ -0,0 +1,55 @@
1
+ from typing import Any, Literal
2
+
3
+ from typing_extensions import Protocol
4
+
5
+ from ..providers.provider import Provider
6
+ from ..tools.function import InputSchema
7
+
8
+
9
+ class UserMessageProtocol(Protocol):
10
+ role: Literal["user"] = "user"
11
+ content: str | list[dict]
12
+
13
+ class FunctionToolResultProtocol(Protocol):
14
+ type: Literal["function_tool_result"] = "function_tool_result"
15
+ call_id: str
16
+ result: Any
17
+ is_error: bool = False
18
+
19
+ class FunctionToolSchemaProtocol(Protocol):
20
+ name: str
21
+ description: str
22
+ input_schema: InputSchema | None
23
+ strict: bool | None = None
24
+ provider_options: dict[str, Any]
25
+
26
+ class ReasoningParamsProtocol(Protocol):
27
+ effort: Literal["none", "minimal", "low", "medium", "high", "extra_high", "max"] | None = None
28
+ summary: Literal["concise", "auto", "detailed"] | None = None
29
+ mode: Literal["standard", "pro"] | None = None
30
+
31
+ class ReasoningProtocol(Protocol):
32
+ type: Literal["reasoning"] = "reasoning"
33
+ text: str | None = None
34
+
35
+ class Adapter:
36
+ def __init__(self, provider: Provider):
37
+ self._provider = provider
38
+
39
+ def map_input_message(self, item: UserMessageProtocol) -> Any:
40
+ raise NotImplementedError("not yet implemented...")
41
+
42
+ def map_tool_result(self, item: FunctionToolResultProtocol) -> Any:
43
+ raise NotImplementedError("not yet implemented...")
44
+
45
+ def map_function_schema(self, tools: list[FunctionToolSchemaProtocol]) -> list:
46
+ raise NotImplementedError("not yet implemented...")
47
+
48
+ def map_reasoning_params(self, item: ReasoningParamsProtocol) -> Any:
49
+ raise NotImplementedError("not yet implemented...")
50
+
51
+ def map_reasoning(self, item: ReasoningProtocol) -> Any:
52
+ raise NotImplementedError("not yet implemented")
53
+
54
+
55
+