requisite-ai 0.3.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.
Files changed (51) hide show
  1. requisite_ai-0.3.0/LICENSE +21 -0
  2. requisite_ai-0.3.0/PKG-INFO +379 -0
  3. requisite_ai-0.3.0/README.md +348 -0
  4. requisite_ai-0.3.0/pyproject.toml +52 -0
  5. requisite_ai-0.3.0/requisite/__init__.py +116 -0
  6. requisite_ai-0.3.0/requisite/agents/__init__.py +6 -0
  7. requisite_ai-0.3.0/requisite/agents/agent.py +318 -0
  8. requisite_ai-0.3.0/requisite/agents/registry.py +66 -0
  9. requisite_ai-0.3.0/requisite/ai.py +329 -0
  10. requisite_ai-0.3.0/requisite/capabilities/__init__.py +28 -0
  11. requisite_ai-0.3.0/requisite/capabilities/registry.py +213 -0
  12. requisite_ai-0.3.0/requisite/capabilities/resolvers.py +166 -0
  13. requisite_ai-0.3.0/requisite/config/__init__.py +5 -0
  14. requisite_ai-0.3.0/requisite/config/settings.py +118 -0
  15. requisite_ai-0.3.0/requisite/core/__init__.py +1 -0
  16. requisite_ai-0.3.0/requisite/core/exceptions.py +116 -0
  17. requisite_ai-0.3.0/requisite/core/interfaces.py +193 -0
  18. requisite_ai-0.3.0/requisite/orchestrators/__init__.py +12 -0
  19. requisite_ai-0.3.0/requisite/orchestrators/base.py +92 -0
  20. requisite_ai-0.3.0/requisite/orchestrators/factory.py +101 -0
  21. requisite_ai-0.3.0/requisite/orchestrators/langgraph_orchestrator.py +148 -0
  22. requisite_ai-0.3.0/requisite/orchestrators/native.py +153 -0
  23. requisite_ai-0.3.0/requisite/providers/__init__.py +12 -0
  24. requisite_ai-0.3.0/requisite/providers/base.py +185 -0
  25. requisite_ai-0.3.0/requisite/providers/factory.py +155 -0
  26. requisite_ai-0.3.0/requisite/providers/gemini_provider.py +362 -0
  27. requisite_ai-0.3.0/requisite/providers/openai_provider.py +316 -0
  28. requisite_ai-0.3.0/requisite/py.typed +0 -0
  29. requisite_ai-0.3.0/requisite/skills/__init__.py +6 -0
  30. requisite_ai-0.3.0/requisite/skills/base.py +81 -0
  31. requisite_ai-0.3.0/requisite/skills/registry.py +64 -0
  32. requisite_ai-0.3.0/requisite/tools/__init__.py +7 -0
  33. requisite_ai-0.3.0/requisite/tools/base.py +136 -0
  34. requisite_ai-0.3.0/requisite/tools/decorator.py +77 -0
  35. requisite_ai-0.3.0/requisite/tools/registry.py +113 -0
  36. requisite_ai-0.3.0/requisite/tools/schema.py +107 -0
  37. requisite_ai-0.3.0/requisite/workflows/__init__.py +6 -0
  38. requisite_ai-0.3.0/requisite/workflows/workflow.py +169 -0
  39. requisite_ai-0.3.0/requisite_ai.egg-info/PKG-INFO +379 -0
  40. requisite_ai-0.3.0/requisite_ai.egg-info/SOURCES.txt +49 -0
  41. requisite_ai-0.3.0/requisite_ai.egg-info/dependency_links.txt +1 -0
  42. requisite_ai-0.3.0/requisite_ai.egg-info/requires.txt +23 -0
  43. requisite_ai-0.3.0/requisite_ai.egg-info/top_level.txt +1 -0
  44. requisite_ai-0.3.0/setup.cfg +4 -0
  45. requisite_ai-0.3.0/tests/test_agents.py +219 -0
  46. requisite_ai-0.3.0/tests/test_ai.py +188 -0
  47. requisite_ai-0.3.0/tests/test_capabilities.py +218 -0
  48. requisite_ai-0.3.0/tests/test_providers.py +285 -0
  49. requisite_ai-0.3.0/tests/test_settings.py +38 -0
  50. requisite_ai-0.3.0/tests/test_tools.py +113 -0
  51. requisite_ai-0.3.0/tests/test_workflows.py +159 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Requisite AI
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.
@@ -0,0 +1,379 @@
1
+ Metadata-Version: 2.4
2
+ Name: requisite-ai
3
+ Version: 0.3.0
4
+ Summary: A provider-agnostic, plugin-based framework for building AI applications and agents. Declare what you need -- providers, tools, capabilities -- not which SDK provides it.
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/requisite-ai/requisite-ai
7
+ Project-URL: Repository, https://github.com/requisite-ai/requisite-ai
8
+ Keywords: ai,agents,llm,openai,gemini,agentic,orchestration,mcp
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: pydantic>=2.7
13
+ Requires-Dist: pydantic-settings>=2.3
14
+ Provides-Extra: openai
15
+ Requires-Dist: openai>=1.35; extra == "openai"
16
+ Provides-Extra: gemini
17
+ Requires-Dist: google-genai>=1.0; extra == "gemini"
18
+ Provides-Extra: langgraph
19
+ Requires-Dist: langgraph>=0.2; extra == "langgraph"
20
+ Provides-Extra: all
21
+ Requires-Dist: openai>=1.35; extra == "all"
22
+ Requires-Dist: google-genai>=1.0; extra == "all"
23
+ Requires-Dist: langgraph>=0.2; extra == "all"
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=8.0; extra == "dev"
26
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
27
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
28
+ Requires-Dist: ruff>=0.5; extra == "dev"
29
+ Requires-Dist: mypy>=1.10; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # Requisite
33
+
34
+ [![CI](https://github.com/requisite-ai/requisite-ai/actions/workflows/ci.yml/badge.svg)](https://github.com/requisite-ai/requisite-ai/actions/workflows/ci.yml)
35
+ [![codecov](https://codecov.io/gh/requisite-ai/requisite-ai/branch/main/graph/badge.svg)](https://codecov.io/gh/requisite-ai/requisite-ai)
36
+ [![PyPI](https://img.shields.io/pypi/v/requisite-ai.svg)](https://pypi.org/project/requisite-ai/)
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
38
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
39
+
40
+ **Declare what your AI application needs — not which SDK provides it.**
41
+
42
+ A provider-agnostic, plugin-based Python framework for building AI
43
+ applications and agents. Swap the LLM provider, the multi-agent execution
44
+ engine, or the implementation behind a capability like `"weather"` or
45
+ `"internet_search"` — all via configuration, never a rewrite.
46
+
47
+ ```python
48
+ from requisite import AI
49
+
50
+ ai = AI() # provider="openai" by default
51
+ ai = AI(provider="gemini", model="gemini-2.5-flash") # same API, different provider
52
+ ```
53
+
54
+ ```python
55
+ from requisite import Agent
56
+
57
+ agent = Agent(name="Assistant", provider="openai")
58
+ agent.requires("weather", "internet_search", "filesystem") # not use_tool(specific_impl)
59
+ agent.run("What's the weather in Tokyo?")
60
+ ```
61
+
62
+ ## Install
63
+
64
+ ```bash
65
+ pip install -e .[all] # both providers + langgraph
66
+ pip install -e .[openai] # OpenAI only
67
+ pip install -e .[gemini] # Gemini only
68
+ pip install -e .[langgraph] # native + langgraph orchestration
69
+ ```
70
+
71
+ Or with the plain requirements file: `pip install -r requirements.txt`
72
+
73
+ > **Note on the Gemini SDK:** this framework uses the current, unified
74
+ > `google-genai` package (`from google import genai`). Do not install the
75
+ > deprecated `google-generativeai` package — the two conflict.
76
+
77
+ ## Configuration
78
+
79
+ Copy `.env.example` to `.env` and fill in the key(s) for the provider(s) you use:
80
+
81
+ ```env
82
+ OPENAI_API_KEY=
83
+ GEMINI_API_KEY=
84
+ DEFAULT_PROVIDER=openai
85
+ MODEL=gpt-4o-mini
86
+ TEMPERATURE=0.2
87
+ ```
88
+
89
+ `Settings` (`requisite/config/settings.py`) reads this automatically — you
90
+ never call `os.environ.get` yourself.
91
+
92
+ ## Usage
93
+
94
+ ### Chat
95
+
96
+ ```python
97
+ from requisite import AI
98
+
99
+ ai = AI()
100
+ print(ai.chat("Explain LangGraph in one sentence."))
101
+ ```
102
+
103
+ ### Structured output
104
+
105
+ ```python
106
+ from pydantic import BaseModel
107
+
108
+ class Person(BaseModel):
109
+ name: str
110
+ age: int
111
+
112
+ person = ai.chat("Extract: John is 30 years old.", response_model=Person)
113
+ print(person.name, person.age) # John 30
114
+ ```
115
+
116
+ ### Tool calling
117
+
118
+ ```python
119
+ from requisite.tools import tool
120
+
121
+ @tool
122
+ def get_weather(city: str) -> str:
123
+ """Get the current weather for a city."""
124
+ return f"Sunny, 22C in {city}"
125
+
126
+ response = ai.chat_response("What's the weather in Paris?", tools=[get_weather])
127
+ if response.has_tool_calls:
128
+ call = response.tool_calls[0]
129
+ result = get_weather.tool.execute(**call.arguments)
130
+ ```
131
+
132
+ That's the low-level view. For most applications, let an `Agent` run the
133
+ full tool-calling loop for you (see below).
134
+
135
+ ### Agents
136
+
137
+ ```python
138
+ from requisite import Agent
139
+ from requisite.tools import tool
140
+
141
+ @tool
142
+ def get_weather(city: str) -> str:
143
+ """Get the current weather for a city."""
144
+ return f"Sunny, 22C in {city}"
145
+
146
+ agent = Agent(name="Weather Agent", provider="openai", tools=[get_weather])
147
+ result = agent.run("What's the weather in Paris?")
148
+ print(result.content) # "It's sunny and 22C in Paris."
149
+ print(result.tool_calls_executed) # ["get_weather"]
150
+ ```
151
+
152
+ `Agent` automatically: offers its tools/skills to the model, executes any
153
+ tool calls the model requests, feeds results back, and repeats (up to
154
+ `max_iterations`) until it has a final answer.
155
+
156
+ ### Multi-agent workflows
157
+
158
+ ```python
159
+ from requisite import Agent, Workflow
160
+
161
+ research = Agent(name="Researcher", provider="openai")
162
+ writer = Agent(name="Writer", provider="openai")
163
+
164
+ workflow = Workflow()
165
+ workflow.add(research)
166
+ workflow.add(writer)
167
+
168
+ result = workflow.run("Research AI trends and write a summary.")
169
+ print(result.content)
170
+ ```
171
+
172
+ Run agents in parallel against the same input instead of as a pipeline:
173
+
174
+ ```python
175
+ workflow.parallel()
176
+ result = workflow.run("What is retrieval-augmented generation?")
177
+ ```
178
+
179
+ Switch the execution engine — the `.add()` / `.run()` API never changes:
180
+
181
+ ```python
182
+ workflow.use_langgraph() # requires: pip install langgraph
183
+ result = workflow.run("Research AI trends and write a summary.")
184
+
185
+ workflow.use_native() # back to the built-in, dependency-free engine
186
+ ```
187
+
188
+ ### Skills
189
+
190
+ A skill is a reusable, higher-level capability (vs. a tool, which is
191
+ typically a single function). Skills expose themselves to the model as
192
+ tools automatically:
193
+
194
+ ```python
195
+ from requisite.skills import BaseSkill
196
+
197
+ class ReadFileSkill(BaseSkill):
198
+ def __init__(self):
199
+ super().__init__(name="read_file", description="Read a text file's contents.")
200
+
201
+ def run(self, path: str) -> str:
202
+ with open(path) as f:
203
+ return f.read()
204
+
205
+ agent = Agent(name="File Agent", provider="openai", skills=[ReadFileSkill()])
206
+ ```
207
+
208
+ ### Capabilities: declare *what*, not *which*
209
+
210
+ `agent.use_tool(specific_impl)` binds an agent to one concrete
211
+ implementation at write-time. `agent.requires("weather")` binds it to a
212
+ *name*, resolved at runtime against whichever implementation is
213
+ currently available — a native tool, an MCP server, a cloud API, or a
214
+ third-party plugin:
215
+
216
+ ```python
217
+ from requisite import Agent
218
+
219
+ agent = Agent(name="Assistant", provider="openai")
220
+ agent.requires("weather", "internet_search", "filesystem")
221
+
222
+ result = agent.run("What's the weather in Tokyo?")
223
+ ```
224
+
225
+ Three capabilities ship out of the box, backed by free/keyless APIs and
226
+ the local filesystem (see `requisite/capabilities/resolvers.py`):
227
+ `"filesystem"`, `"weather"`, `"internet_search"`.
228
+
229
+ Register a better provider for the same capability at a higher priority
230
+ and it takes over automatically — application code never changes:
231
+
232
+ ```python
233
+ from requisite.capabilities import default_registry
234
+
235
+ default_registry.register(
236
+ "weather",
237
+ my_paid_weather_tool,
238
+ provider_name="acme-weather",
239
+ priority=10,
240
+ is_available=lambda: bool(os.environ.get("ACME_API_KEY")),
241
+ )
242
+ # agent.requires("weather") now resolves to "acme-weather" when the key
243
+ # is set, and quietly falls back to the built-in provider otherwise.
244
+ ```
245
+
246
+ This is the same interface + registry pattern used for providers and
247
+ orchestrators, one layer up: `CapabilityRegistry.resolve(name)` picks the
248
+ highest-priority provider whose `is_available()` currently returns
249
+ `True`, raising `CapabilityException` if none are.
250
+
251
+ ### Streaming & async
252
+
253
+ ```python
254
+ for token in ai.stream("Write a haiku about distributed systems."):
255
+ print(token, end="")
256
+
257
+ text = await ai.achat("Hello!")
258
+ async for token in ai.astream("Hello, streamed!"):
259
+ print(token, end="")
260
+
261
+ result = await agent.arun("What's the weather in Paris?")
262
+ result = await workflow.arun("Research AI trends.")
263
+ ```
264
+
265
+ ### Conversation history
266
+
267
+ ```python
268
+ from requisite import Message
269
+
270
+ history = [
271
+ Message.user("My name is Alex."),
272
+ Message.assistant("Nice to meet you, Alex!"),
273
+ Message.user("What's my name?"),
274
+ ]
275
+ print(ai.chat(history))
276
+ ```
277
+
278
+ See `examples/` for complete, runnable scripts covering each of the above.
279
+
280
+ ## Architecture
281
+
282
+ ```
283
+ requisite/
284
+ ├── core/ # Provider-agnostic data models (Message, ChatResponse,
285
+ │ # ToolCall, ...) and the AIException hierarchy
286
+ ├── config/ # Settings (pydantic-settings, reads .env)
287
+ ├── providers/ # BaseProvider interface + OpenAI/Gemini implementations
288
+ │ # + ProviderRegistry (extensible, DI-friendly factory)
289
+ ├── tools/ # Tool, @tool decorator, ToolRegistry, JSON Schema derivation
290
+ ├── skills/ # BaseSkill, SkillRegistry -- reusable higher-level capabilities
291
+ ├── capabilities/ # CapabilityRegistry -- resolve a named capability (e.g.
292
+ │ # "weather") to whichever implementation is available
293
+ ├── agents/ # Agent (tool-calling loop, .requires()) + AgentRegistry
294
+ ├── orchestrators/ # BaseOrchestrator interface + native (sequential/parallel)
295
+ │ # and langgraph backends + OrchestratorRegistry
296
+ ├── workflows/ # Workflow -- the small, ergonomic multi-agent facade
297
+ └── ai.py # The `AI` facade -- the class most users touch directly
298
+ ```
299
+
300
+ Every layer follows the same pattern: a small abstract interface
301
+ (`BaseProvider`, `BaseOrchestrator`, ...), one or more concrete
302
+ implementations, and a plain, instantiable registry (not a singleton)
303
+ mapping names to constructors. That's what makes each of the following a
304
+ *configuration* change rather than a *code* change:
305
+
306
+ - `AI(provider="openai")` → `AI(provider="gemini")`
307
+ - `Workflow(orchestrator="native")` → `workflow.use_langgraph()`
308
+ - `agent.use_tool(specific_impl)` → `agent.requires("weather")`
309
+
310
+ See **[`ARCHITECTURE.md`](ARCHITECTURE.md)** for the full dependency
311
+ diagram, request-flow walkthroughs (a chat call, an agent's tool-calling
312
+ loop, capability resolution, a multi-agent workflow), and the design
313
+ decisions behind them. See **[`CONTRIBUTING.md`](CONTRIBUTING.md)** for
314
+ the step-by-step to add a new provider, orchestrator backend, or
315
+ capability resolver.
316
+
317
+ ## Error handling
318
+
319
+ All framework exceptions inherit from `AIException`:
320
+
321
+ ```
322
+ AIException
323
+ ├── ConfigurationException # missing/invalid config, unknown provider/orchestrator name
324
+ ├── ProviderException # provider SDK call failed (wraps the original error)
325
+ ├── ToolException # a tool wasn't found, or raised while executing
326
+ ├── SkillException # a skill wasn't found, or raised while executing
327
+ ├── AgentException # agent execution failed (e.g. max_iterations exceeded)
328
+ ├── CapabilityException # a required capability has no available provider
329
+ └── MCPException # reserved for the upcoming MCP integration
330
+ ```
331
+
332
+ Provider SDK errors are never swallowed — they're wrapped with `provider`
333
+ and `original_error` attached, and re-raised via `raise ... from original_error`
334
+ so the original traceback is preserved.
335
+
336
+ ## Testing
337
+
338
+ ```bash
339
+ pytest
340
+ ```
341
+
342
+ Tests never hit the network: the OpenAI and Gemini SDKs are faked via
343
+ `sys.modules` injection, and the `AI` / `Agent` / `Workflow` facades are
344
+ tested against fully in-memory fake providers.
345
+
346
+ ## Roadmap
347
+
348
+ Implemented: provider connectivity (OpenAI, Gemini), structured outputs,
349
+ tool calling, skills, capability resolution (`agent.requires(...)`),
350
+ agents + registry, multi-agent workflows (sequential/parallel, native +
351
+ langgraph backends).
352
+
353
+ See [`ROADMAP.md`](ROADMAP.md) for the full, per-layer status table
354
+ (providers, orchestration strategies, MCP, memory, RAG, ...) and what's
355
+ explicitly out of scope.
356
+
357
+ ## Contributing
358
+
359
+ Contributions are welcome:
360
+
361
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md) — dev setup, running checks, and
362
+ step-by-step guides for adding a provider, orchestrator backend, or
363
+ capability resolver.
364
+ - [`ARCHITECTURE.md`](ARCHITECTURE.md) — how the framework fits together
365
+ and why: the interface + registry pattern, request-flow walkthroughs,
366
+ design decisions.
367
+ - [`DEVELOPMENT.md`](DEVELOPMENT.md) — coding standards, testing
368
+ philosophy, docstring format, logging/error-handling conventions,
369
+ versioning policy.
370
+ - [`ROADMAP.md`](ROADMAP.md) — what's shipped, planned, or out of scope.
371
+
372
+ Please also read the [Code of Conduct](CODE_OF_CONDUCT.md). Security
373
+ issues should go through [`SECURITY.md`](SECURITY.md), not a public issue.
374
+
375
+ See [`CHANGELOG.md`](CHANGELOG.md) for release history.
376
+
377
+ ## License
378
+
379
+ MIT — see [`LICENSE`](LICENSE).