askai-python 0.4.1__tar.gz → 0.6.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 (65) hide show
  1. {askai_python-0.4.1/askai_python.egg-info → askai_python-0.6.0}/PKG-INFO +34 -10
  2. {askai_python-0.4.1 → askai_python-0.6.0}/README.md +177 -5
  3. askai_python-0.6.0/ask_ai/__init__.py +81 -0
  4. askai_python-0.6.0/ask_ai/base.py +881 -0
  5. askai_python-0.6.0/ask_ai/capabilities.py +17 -0
  6. {askai_python-0.4.1 → askai_python-0.6.0}/ask_ai/cli.py +5 -19
  7. askai_python-0.6.0/ask_ai/config.py +139 -0
  8. askai_python-0.6.0/ask_ai/events.py +41 -0
  9. askai_python-0.6.0/ask_ai/exceptions.py +128 -0
  10. askai_python-0.6.0/ask_ai/media.py +477 -0
  11. askai_python-0.6.0/ask_ai/media_security.py +152 -0
  12. askai_python-0.6.0/ask_ai/observability.py +84 -0
  13. askai_python-0.6.0/ask_ai/providers/anthropic_provider.py +223 -0
  14. {askai_python-0.4.1 → askai_python-0.6.0}/ask_ai/providers/azure_provider.py +13 -4
  15. askai_python-0.6.0/ask_ai/providers/google_provider.py +255 -0
  16. askai_python-0.6.0/ask_ai/providers/groq_provider.py +225 -0
  17. askai_python-0.6.0/ask_ai/providers/openai_provider.py +387 -0
  18. {askai_python-0.4.1 → askai_python-0.6.0}/ask_ai/providers/openrouter_provider.py +41 -14
  19. askai_python-0.6.0/ask_ai/py.typed +0 -0
  20. askai_python-0.6.0/ask_ai/requirements.txt +6 -0
  21. askai_python-0.6.0/ask_ai/router.py +107 -0
  22. askai_python-0.6.0/ask_ai/session.py +140 -0
  23. askai_python-0.6.0/ask_ai/structured.py +309 -0
  24. {askai_python-0.4.1 → askai_python-0.6.0}/ask_ai/utils.py +3 -4
  25. {askai_python-0.4.1 → askai_python-0.6.0/askai_python.egg-info}/PKG-INFO +34 -10
  26. {askai_python-0.4.1 → askai_python-0.6.0}/askai_python.egg-info/SOURCES.txt +26 -1
  27. {askai_python-0.4.1 → askai_python-0.6.0}/askai_python.egg-info/requires.txt +10 -4
  28. {askai_python-0.4.1 → askai_python-0.6.0}/docs/PYPI_README.md +23 -3
  29. {askai_python-0.4.1 → askai_python-0.6.0}/pyproject.toml +12 -9
  30. askai_python-0.6.0/tests/test_capabilities.py +17 -0
  31. askai_python-0.6.0/tests/test_core.py +691 -0
  32. askai_python-0.6.0/tests/test_gitignore.py +20 -0
  33. askai_python-0.6.0/tests/test_google_genai.py +189 -0
  34. askai_python-0.6.0/tests/test_media_security.py +265 -0
  35. askai_python-0.6.0/tests/test_native_structured.py +471 -0
  36. askai_python-0.6.0/tests/test_observability.py +74 -0
  37. askai_python-0.6.0/tests/test_openrouter_release.py +70 -0
  38. askai_python-0.6.0/tests/test_options_validation.py +136 -0
  39. askai_python-0.6.0/tests/test_packaging.py +8 -0
  40. askai_python-0.6.0/tests/test_python39_compat.py +13 -0
  41. askai_python-0.6.0/tests/test_release_docs.py +46 -0
  42. askai_python-0.6.0/tests/test_router.py +60 -0
  43. askai_python-0.6.0/tests/test_session.py +129 -0
  44. askai_python-0.6.0/tests/test_smoke_real.py +63 -0
  45. askai_python-0.6.0/tests/test_streaming_events.py +142 -0
  46. askai_python-0.6.0/tests/test_structured.py +91 -0
  47. askai_python-0.6.0/tests/test_usage.py +359 -0
  48. askai_python-0.4.1/ask_ai/__init__.py +0 -51
  49. askai_python-0.4.1/ask_ai/base.py +0 -444
  50. askai_python-0.4.1/ask_ai/config.py +0 -81
  51. askai_python-0.4.1/ask_ai/exceptions.py +0 -42
  52. askai_python-0.4.1/ask_ai/media.py +0 -90
  53. askai_python-0.4.1/ask_ai/providers/anthropic_provider.py +0 -128
  54. askai_python-0.4.1/ask_ai/providers/google_provider.py +0 -150
  55. askai_python-0.4.1/ask_ai/providers/groq_provider.py +0 -114
  56. askai_python-0.4.1/ask_ai/providers/openai_provider.py +0 -257
  57. askai_python-0.4.1/ask_ai/requirements.txt +0 -5
  58. askai_python-0.4.1/tests/test_core.py +0 -38
  59. {askai_python-0.4.1 → askai_python-0.6.0}/LICENSE +0 -0
  60. {askai_python-0.4.1 → askai_python-0.6.0}/ask_ai/__main__.py +0 -0
  61. {askai_python-0.4.1 → askai_python-0.6.0}/ask_ai/providers/__init__.py +4 -4
  62. {askai_python-0.4.1 → askai_python-0.6.0}/askai_python.egg-info/dependency_links.txt +0 -0
  63. {askai_python-0.4.1 → askai_python-0.6.0}/askai_python.egg-info/entry_points.txt +0 -0
  64. {askai_python-0.4.1 → askai_python-0.6.0}/askai_python.egg-info/top_level.txt +0 -0
  65. {askai_python-0.4.1 → askai_python-0.6.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: askai-python
3
- Version: 0.4.1
3
+ Version: 0.6.0
4
4
  Summary: AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, Anthropic, and more.
5
5
  Author-email: Hossein Ghorbani <hosseingh1068@gmail.com>
6
6
  License: MIT
@@ -13,7 +13,6 @@ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.9
17
16
  Classifier: Programming Language :: Python :: 3.10
18
17
  Classifier: Programming Language :: Python :: 3.11
19
18
  Classifier: Programming Language :: Python :: 3.12
@@ -21,28 +20,33 @@ Classifier: Programming Language :: Python :: 3.13
21
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
21
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
22
  Classifier: Typing :: Typed
24
- Requires-Python: >=3.9
23
+ Requires-Python: >=3.10
25
24
  Description-Content-Type: text/markdown
26
25
  License-File: LICENSE
27
26
  Provides-Extra: openai
28
27
  Requires-Dist: openai>=1.0.0; extra == "openai"
29
28
  Provides-Extra: anthropic
30
- Requires-Dist: anthropic>=0.20.0; extra == "anthropic"
29
+ Requires-Dist: anthropic>=1.0.0; extra == "anthropic"
31
30
  Provides-Extra: google
32
- Requires-Dist: google-generativeai>=0.5.0; extra == "google"
31
+ Requires-Dist: google-genai>=1.0.0; extra == "google"
33
32
  Provides-Extra: groq
34
33
  Requires-Dist: groq>=0.4.0; extra == "groq"
35
34
  Provides-Extra: media
36
35
  Requires-Dist: Pillow>=9.0.0; extra == "media"
37
36
  Provides-Extra: httpx
38
37
  Requires-Dist: httpx>=0.24.0; extra == "httpx"
38
+ Provides-Extra: structured
39
+ Requires-Dist: pydantic>=1.10; extra == "structured"
40
+ Requires-Dist: jsonschema>=4.0; extra == "structured"
39
41
  Provides-Extra: all
40
42
  Requires-Dist: openai>=1.0.0; extra == "all"
41
- Requires-Dist: anthropic>=0.20.0; extra == "all"
42
- Requires-Dist: google-generativeai>=0.5.0; extra == "all"
43
+ Requires-Dist: anthropic>=1.0.0; extra == "all"
44
+ Requires-Dist: google-genai>=1.0.0; extra == "all"
43
45
  Requires-Dist: groq>=0.4.0; extra == "all"
44
46
  Requires-Dist: Pillow>=9.0.0; extra == "all"
45
47
  Requires-Dist: httpx>=0.24.0; extra == "all"
48
+ Requires-Dist: pydantic>=1.10; extra == "all"
49
+ Requires-Dist: jsonschema>=4.0; extra == "all"
46
50
  Dynamic: license-file
47
51
 
48
52
  # askai-python 🚀
@@ -59,9 +63,16 @@ No frameworks. No servers. No overengineering.
59
63
  ## ⚡ Quick Start (5 seconds)
60
64
 
61
65
  ```bash
62
- pip install askai-python
66
+ pip install "askai-python[openai,groq]"
63
67
  ```
64
68
 
69
+ Provider SDKs are optional. Install the extras you need:
70
+
71
+ - `pip install "askai-python[openai]"` for OpenAI, Azure, and OpenRouter.
72
+ - `pip install "askai-python[anthropic]"`, `pip install "askai-python[google]"`,
73
+ or `pip install "askai-python[groq]"` for those providers.
74
+ - `pip install "askai-python[all]"` for every provider SDK.
75
+
65
76
  ```python
66
77
  from ask_ai import OpenAI, Groq
67
78
 
@@ -197,20 +208,33 @@ print(user.name) # "Alice"
197
208
  |----------|-------|-------------|
198
209
  | **OpenAI** | `OpenAI` | Text, Images (DALL-E), Vision |
199
210
  | **Anthropic** | `Anthropic` | Text, Vision (Claude 3.5) |
200
- | **Google** | `Google` | Text, Images, Video, Audio |
211
+ | **Google** | `Google` | Text, Vision |
201
212
  | **Groq** | `Groq` | Ultra-fast Llama 3 & Mixtral |
202
213
  | **Azure** | `Azure` | Enterprise-grade OpenAI |
203
214
  | **OpenRouter**| `OpenRouter`| 100+ community models |
204
215
 
205
216
  ---
206
217
 
218
+ ## Conversation Sessions
219
+
220
+ Sessions provide optional in-memory conversation history:
221
+
222
+ ```python
223
+ session = OpenAI().session(max_messages=20)
224
+ session.ask("My name is Ada.")
225
+ print(session.ask("What is my name?").text)
226
+ ```
227
+
228
+ Session state is not persisted, and session requests do not support
229
+ `response_model` validation yet.
230
+
207
231
  ## 🗺️ Roadmap
208
232
 
209
233
  ### 🚀 Roadmap 2.0 (Active)
210
234
  - [x] Provider Fallback chaining & Pydantic Structured Output support
235
+ - [x] Optional in-memory conversation sessions
211
236
  - [ ] Multi-Modal Vision input (image to text) for all key providers
212
237
  - [ ] Native support for tools/function calling execution
213
- - [ ] Memory buffer / session-based conversation management
214
238
 
215
239
  ### 🏁 Roadmap 1.0 (Completed)
216
240
  - ~~[x] Baseline multiple providers~~
@@ -22,9 +22,10 @@
22
22
  No frameworks. No servers. No overengineering.
23
23
  </p>
24
24
 
25
+
25
26
  [![PyPI version](https://img.shields.io/pypi/v/askai-python.svg)](https://pypi.org/project/askai-python/)
26
27
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
27
- [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
28
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
28
29
 
29
30
 
30
31
 
@@ -33,9 +34,18 @@
33
34
  ## ⚡ Quick Start (5 seconds)
34
35
 
35
36
  ```bash
36
- pip install askai-python
37
+ pip install "askai-python[openai,groq]"
37
38
  ```
38
39
 
40
+ Provider SDKs are optional dependencies. Install the extras you need:
41
+
42
+ - `pip install "askai-python[openai]"` for OpenAI, Azure, and OpenRouter.
43
+ - `pip install "askai-python[anthropic]"`, `pip install "askai-python[google]"`,
44
+ or `pip install "askai-python[groq]"` for those providers.
45
+ - `pip install "askai-python[all]"` for every provider SDK.
46
+
47
+ Python 3.10 or newer is required.
48
+
39
49
  ```python
40
50
  from ask_ai import OpenAI, Groq
41
51
 
@@ -111,6 +121,12 @@ ai = OpenAI()
111
121
  response = ai.ask("Write a script", retry=3, timeout=10)
112
122
  ```
113
123
 
124
+ Requests default to `retry=2` (up to three attempts) for retryable timeout,
125
+ connection, rate-limit (`429`), and server (`5xx`) errors. Authentication and
126
+ invalid-request errors are not retried; use `retry=0` to disable retries.
127
+ Streaming retries occur only before the first chunk/event, so already-emitted
128
+ output is never duplicated.
129
+
114
130
  ### System Configuration
115
131
  Set system prompts and temperature directly:
116
132
 
@@ -123,6 +139,11 @@ ai.advanced(
123
139
  print(ai.ask("How do I optimize a Dockerfile?").text)
124
140
  ```
125
141
 
142
+ Unknown option names produce a `UserWarning` with a spelling suggestion. Pass
143
+ `strict_options=True` to a provider (or to an individual request) to raise a
144
+ `ValueError` instead. `safe_mode` currently has no defined behavior and is
145
+ deprecated; passing it emits a `DeprecationWarning`.
146
+
126
147
  ---
127
148
 
128
149
  ### 🚀 Async Support (FastAPI / Aiogram)
@@ -159,7 +180,7 @@ for chunk in ai.ask_stream("Write a short story about a blue dragon"):
159
180
 
160
181
  # Async Streaming
161
182
  async def stream_example():
162
- async for chunk in await ai.ask_stream_async("Write a short story"):
183
+ async for chunk in ai.ask_stream_async("Write a short story"):
163
184
  print(chunk, end="", flush=True)
164
185
  ```
165
186
 
@@ -221,15 +242,166 @@ print(user.name) # "Alice"
221
242
  print(user.age) # 30
222
243
  ```
223
244
 
245
+ ### ✅ Validated Structured Responses
246
+
247
+ Structured responses support Pydantic models, dataclasses, `TypedDict`, and JSON
248
+ Schema dictionaries. The raw provider text remains available separately:
249
+
250
+ ```python
251
+ from dataclasses import dataclass
252
+ from ask_ai import OpenAI
253
+
254
+ @dataclass
255
+ class User:
256
+ name: str
257
+ age: int
258
+
259
+ response = OpenAI().ask(
260
+ "Extract: Alice is 30.",
261
+ response_model=User,
262
+ validation_retries=2,
263
+ )
264
+ print(response.raw)
265
+ print(response.parsed.name)
266
+ ```
267
+
268
+ For supported schemas, `response_model` also requests native JSON Schema output
269
+ from OpenAI/Azure, Anthropic, Groq, Google, and OpenRouter. When an adapter
270
+ receives a clear unsupported-schema response (HTTP 400/422), it retries once
271
+ without the native schema and keeps local validation enabled. Authentication,
272
+ rate-limit, and server errors are propagated, not retried as prompt-only
273
+ fallbacks. Schemas that cannot be represented in the provider's strict schema
274
+ format use prompt instructions and local validation directly. Provider/model
275
+ support varies; these request parameters have mock-based tests but have not
276
+ been verified against live APIs.
277
+
278
+ Provider parameters follow their documented APIs:
279
+ [OpenAI Chat Completions](https://developers.openai.com/api/docs/guides/structured-outputs),
280
+ [Azure OpenAI](https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/structured-outputs),
281
+ [Anthropic](https://platform.claude.com/docs/en/build-with-claude/structured-outputs),
282
+ [Groq](https://console.groq.com/docs/structured-outputs),
283
+ [Google GenAI](https://ai.google.dev/gemini-api/docs/structured-output), and
284
+ [OpenRouter Chat Completions](https://openrouter.ai/docs/guides/features/structured-outputs).
285
+
286
+ ### Streaming Events
287
+
288
+ Use typed events for text, completion, and errors while keeping the legacy
289
+ `ask_stream()` API unchanged:
290
+
291
+ ```python
292
+ from ask_ai import Completed, OpenAI, TextDelta
293
+
294
+ for event in OpenAI().ask_stream_events("Tell me a story"):
295
+ if isinstance(event, TextDelta):
296
+ print(event.text, end="")
297
+ elif isinstance(event, Completed):
298
+ print("\nDone")
299
+ ```
300
+
301
+ `UsageUpdate` events are emitted when an adapter receives usage in its stream.
302
+ `ToolCall` is a reserved event type and is not emitted by the current adapters.
303
+ Streaming usage extraction is mock-tested and has not been verified against
304
+ live provider APIs.
305
+
306
+ ### 📊 Observability
307
+
308
+ Attach optional middleware to receive provider, model, latency, retry, and
309
+ provider-reported token usage. Usage field names are normalized to
310
+ `input_tokens`, `output_tokens`, and `total_tokens`. Estimated cost is only
311
+ calculated when usage is available and pricing for the model is supplied;
312
+ missing usage never produces a cost estimate. Mock tests cover usage extraction
313
+ for supported adapters; live provider APIs have not been verified. On failures,
314
+ middleware emits a `failed` event with `failure_reason`, then the exception is
315
+ propagated:
316
+
317
+ ```python
318
+ from ask_ai import Observability, OpenAI
319
+
320
+ ai = OpenAI().use(Observability(on_event=print))
321
+ response = ai.ask("Explain tracing")
322
+ print(response.metadata.latency_ms)
323
+ print(response.metadata.usage)
324
+ ```
325
+
326
+ ### Provider Capabilities
327
+
328
+ Inspect advisory capability metadata before selecting a provider:
329
+
330
+ ```python
331
+ from ask_ai import OpenAI
332
+
333
+ capabilities = OpenAI().capabilities()
334
+ if capabilities.structured_output:
335
+ print("Structured output is available")
336
+ ```
337
+
338
+ Capabilities are advisory provider-level metadata, not guarantees for every
339
+ model.
340
+
341
+ ### Smart Router
342
+
343
+ Route requests across providers using deterministic `cheapest`, `fastest`, or
344
+ `most_reliable` policies. Supply static `profiles` for meaningful ordering:
345
+
346
+ ```python
347
+ from ask_ai import Groq, OpenAI, Router
348
+
349
+ openai = OpenAI() # Requires OPENAI_API_KEY
350
+ groq = Groq() # Requires GROQ_API_KEY
351
+ router = Router(
352
+ [openai, groq],
353
+ policy="fastest",
354
+ profiles={
355
+ "OpenAIProvider": {"latency_ms": 100},
356
+ "GroqProvider": {"latency_ms": 50},
357
+ },
358
+ )
359
+ response = router.ask("Explain routing")
360
+ ```
361
+
362
+ ### 🔒 Secure Media Downloads
363
+
364
+ Provider media downloads can be constrained by HTTPS, host, detected MIME, size,
365
+ and timeout policy. DNS results resolving to private, loopback, link-local, or
366
+ reserved IP addresses are rejected by default. `allowed_hosts` is optional;
367
+ set it for an explicit hostname allowlist. Only opt in to `allow_private=True`
368
+ when downloading from trusted private-network hosts. Redirects are rejected:
369
+
370
+ ```python
371
+ from ask_ai import MediaPolicy, download_media
372
+
373
+ data = download_media(
374
+ "https://example.com/image.png",
375
+ MediaPolicy(
376
+ max_bytes=10 * 1024 * 1024,
377
+ allowed_mime_types={"image/png"},
378
+ allowed_hosts={"example.com"},
379
+ ),
380
+ )
381
+ ```
382
+
383
+ ### Conversation Sessions
384
+
385
+ Create optional in-memory conversation state without changing the normal
386
+ provider API:
387
+
388
+ ```python
389
+ session = OpenAI().session(max_messages=20)
390
+ session.ask("My name is Ada.")
391
+ print(session.ask("What is my name?").text)
392
+ ```
393
+
394
+ Session requests do not support `response_model` validation yet.
395
+
224
396
  ---
225
397
 
226
398
  ## 🗺️ Roadmap
227
399
 
228
400
  ### 🚀 Roadmap 2.0 (Active)
229
401
  - [x] Provider Fallback chaining & Pydantic Structured Output support
230
- - [ ] Multi-Modal Vision input (image to text) for all key providers
402
+ - [x] Multi-Modal Vision input (image to text) for all key providers
231
403
  - [ ] Native support for tools/function calling execution
232
- - [ ] Memory buffer / session-based conversation management
404
+ - [x] Optional in-memory session-based conversation management
233
405
 
234
406
  ### 🏁 Roadmap 1.0 (Completed)
235
407
  - ~~[x] OpenAI, Groq, Gemini, Anthropic, Azure, OpenRouter support~~
@@ -0,0 +1,81 @@
1
+ """
2
+ ask-ai: A minimal Python SDK to switch between LLM providers in one line.
3
+ """
4
+
5
+ __version__ = "0.6.0"
6
+
7
+ from .base import Response
8
+ from .capabilities import Capabilities
9
+ from .config import AdvancedConfig
10
+ from .events import Completed, Error, TextDelta, ToolCall, UsageUpdate
11
+ from .exceptions import (
12
+ AskAIConfigError,
13
+ AskAIError,
14
+ AskAINetworkError,
15
+ AskAIParsingError,
16
+ AskAIRateLimitError,
17
+ MediaSecurityError,
18
+ MediaTypeNotSupportedError,
19
+ ProviderAuthError,
20
+ ProviderConnectionError,
21
+ ProviderError,
22
+ ProviderInvalidRequestError,
23
+ ProviderRateLimitError,
24
+ ProviderTimeoutError,
25
+ ProviderUnavailableError,
26
+ StreamNotSupportedError,
27
+ StructuredValidationError,
28
+ )
29
+ from .media import AudioObject, ImageObject
30
+ from .media_security import MediaPolicy, download_media
31
+ from .observability import Observability, ResponseMetadata, Usage
32
+ from .providers import Anthropic, Azure, Google, Groq, OpenAI, OpenRouter
33
+ from .router import Router
34
+ from .session import ConversationSession
35
+ from .structured import parse_structured
36
+
37
+ __all__ = [
38
+ # Providers
39
+ "OpenAI",
40
+ "Anthropic",
41
+ "Google",
42
+ "Groq",
43
+ "Azure",
44
+ "OpenRouter",
45
+ # Core
46
+ "AdvancedConfig",
47
+ "Response",
48
+ "ImageObject",
49
+ "AudioObject",
50
+ # Exceptions
51
+ "AskAIError",
52
+ "AskAIConfigError",
53
+ "AskAINetworkError",
54
+ "AskAIRateLimitError",
55
+ "AskAIParsingError",
56
+ "ProviderError",
57
+ "ProviderAuthError",
58
+ "ProviderConnectionError",
59
+ "ProviderRateLimitError",
60
+ "ProviderTimeoutError",
61
+ "ProviderInvalidRequestError",
62
+ "ProviderUnavailableError",
63
+ "MediaTypeNotSupportedError",
64
+ "MediaSecurityError",
65
+ "StreamNotSupportedError",
66
+ "StructuredValidationError",
67
+ "parse_structured",
68
+ "TextDelta",
69
+ "ToolCall",
70
+ "UsageUpdate",
71
+ "Completed",
72
+ "Error",
73
+ "Observability",
74
+ "ResponseMetadata",
75
+ "Usage",
76
+ "Capabilities",
77
+ "Router",
78
+ "ConversationSession",
79
+ "MediaPolicy",
80
+ "download_media",
81
+ ]