askai-python 0.4.1__tar.gz → 0.5.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. {askai_python-0.4.1/askai_python.egg-info → askai_python-0.5.0}/PKG-INFO +6 -1
  2. {askai_python-0.4.1 → askai_python-0.5.0}/README.md +124 -3
  3. askai_python-0.5.0/ask_ai/__init__.py +79 -0
  4. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/base.py +298 -36
  5. askai_python-0.5.0/ask_ai/capabilities.py +17 -0
  6. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/cli.py +5 -19
  7. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/config.py +21 -11
  8. askai_python-0.5.0/ask_ai/events.py +41 -0
  9. askai_python-0.5.0/ask_ai/exceptions.py +113 -0
  10. askai_python-0.5.0/ask_ai/media.py +477 -0
  11. askai_python-0.5.0/ask_ai/media_security.py +105 -0
  12. askai_python-0.5.0/ask_ai/observability.py +53 -0
  13. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/providers/anthropic_provider.py +31 -16
  14. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/providers/azure_provider.py +4 -2
  15. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/providers/google_provider.py +27 -14
  16. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/providers/groq_provider.py +22 -14
  17. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/providers/openai_provider.py +80 -53
  18. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/providers/openrouter_provider.py +31 -12
  19. askai_python-0.5.0/ask_ai/requirements.txt +6 -0
  20. askai_python-0.5.0/ask_ai/router.py +107 -0
  21. askai_python-0.5.0/ask_ai/session.py +114 -0
  22. askai_python-0.5.0/ask_ai/structured.py +88 -0
  23. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/utils.py +3 -4
  24. {askai_python-0.4.1 → askai_python-0.5.0/askai_python.egg-info}/PKG-INFO +6 -1
  25. {askai_python-0.4.1 → askai_python-0.5.0}/askai_python.egg-info/SOURCES.txt +18 -1
  26. {askai_python-0.4.1 → askai_python-0.5.0}/askai_python.egg-info/requires.txt +6 -0
  27. {askai_python-0.4.1 → askai_python-0.5.0}/pyproject.toml +4 -1
  28. askai_python-0.5.0/tests/test_capabilities.py +17 -0
  29. askai_python-0.5.0/tests/test_core.py +266 -0
  30. askai_python-0.5.0/tests/test_gitignore.py +12 -0
  31. askai_python-0.5.0/tests/test_media_security.py +193 -0
  32. askai_python-0.5.0/tests/test_observability.py +74 -0
  33. askai_python-0.5.0/tests/test_openrouter_release.py +70 -0
  34. askai_python-0.5.0/tests/test_python39_compat.py +13 -0
  35. askai_python-0.5.0/tests/test_router.py +60 -0
  36. askai_python-0.5.0/tests/test_session.py +81 -0
  37. askai_python-0.5.0/tests/test_streaming_events.py +79 -0
  38. askai_python-0.5.0/tests/test_structured.py +91 -0
  39. askai_python-0.4.1/ask_ai/__init__.py +0 -51
  40. askai_python-0.4.1/ask_ai/exceptions.py +0 -42
  41. askai_python-0.4.1/ask_ai/media.py +0 -90
  42. askai_python-0.4.1/ask_ai/requirements.txt +0 -5
  43. askai_python-0.4.1/tests/test_core.py +0 -38
  44. {askai_python-0.4.1 → askai_python-0.5.0}/LICENSE +0 -0
  45. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/__main__.py +0 -0
  46. {askai_python-0.4.1 → askai_python-0.5.0}/ask_ai/providers/__init__.py +4 -4
  47. {askai_python-0.4.1 → askai_python-0.5.0}/askai_python.egg-info/dependency_links.txt +0 -0
  48. {askai_python-0.4.1 → askai_python-0.5.0}/askai_python.egg-info/entry_points.txt +0 -0
  49. {askai_python-0.4.1 → askai_python-0.5.0}/askai_python.egg-info/top_level.txt +0 -0
  50. {askai_python-0.4.1 → askai_python-0.5.0}/docs/PYPI_README.md +0 -0
  51. {askai_python-0.4.1 → askai_python-0.5.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.5.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
@@ -36,6 +36,9 @@ Provides-Extra: media
36
36
  Requires-Dist: Pillow>=9.0.0; extra == "media"
37
37
  Provides-Extra: httpx
38
38
  Requires-Dist: httpx>=0.24.0; extra == "httpx"
39
+ Provides-Extra: structured
40
+ Requires-Dist: pydantic>=1.10; extra == "structured"
41
+ Requires-Dist: jsonschema>=4.0; extra == "structured"
39
42
  Provides-Extra: all
40
43
  Requires-Dist: openai>=1.0.0; extra == "all"
41
44
  Requires-Dist: anthropic>=0.20.0; extra == "all"
@@ -43,6 +46,8 @@ Requires-Dist: google-generativeai>=0.5.0; extra == "all"
43
46
  Requires-Dist: groq>=0.4.0; extra == "all"
44
47
  Requires-Dist: Pillow>=9.0.0; extra == "all"
45
48
  Requires-Dist: httpx>=0.24.0; extra == "all"
49
+ Requires-Dist: pydantic>=1.10; extra == "all"
50
+ Requires-Dist: jsonschema>=4.0; extra == "all"
46
51
  Dynamic: license-file
47
52
 
48
53
  # askai-python 🚀
@@ -22,6 +22,7 @@
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
28
  [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
@@ -159,7 +160,7 @@ for chunk in ai.ask_stream("Write a short story about a blue dragon"):
159
160
 
160
161
  # Async Streaming
161
162
  async def stream_example():
162
- async for chunk in await ai.ask_stream_async("Write a short story"):
163
+ async for chunk in ai.ask_stream_async("Write a short story"):
163
164
  print(chunk, end="", flush=True)
164
165
  ```
165
166
 
@@ -221,15 +222,135 @@ print(user.name) # "Alice"
221
222
  print(user.age) # 30
222
223
  ```
223
224
 
225
+ ### ✅ Validated Structured Responses
226
+
227
+ Structured responses support Pydantic models, dataclasses, `TypedDict`, and JSON
228
+ Schema dictionaries. The raw provider text remains available separately:
229
+
230
+ ```python
231
+ from dataclasses import dataclass
232
+ from ask_ai import OpenAI
233
+
234
+ @dataclass
235
+ class User:
236
+ name: str
237
+ age: int
238
+
239
+ response = OpenAI().ask(
240
+ "Extract: Alice is 30.",
241
+ response_model=User,
242
+ validation_retries=2,
243
+ )
244
+ print(response.raw)
245
+ print(response.parsed.name)
246
+ ```
247
+
248
+ ### Streaming Events
249
+
250
+ Use typed events for text, completion, and errors while keeping the legacy
251
+ `ask_stream()` API unchanged:
252
+
253
+ ```python
254
+ from ask_ai import Completed, OpenAI, TextDelta
255
+
256
+ for event in OpenAI().ask_stream_events("Tell me a story"):
257
+ if isinstance(event, TextDelta):
258
+ print(event.text, end="")
259
+ elif isinstance(event, Completed):
260
+ print("\nDone")
261
+ ```
262
+
263
+ ### 📊 Observability
264
+
265
+ Attach optional middleware to receive provider, model, latency, and retry
266
+ metadata. Provider usage extraction and failure-reason reporting are not
267
+ supported yet. Estimated cost is only calculated when a response already
268
+ contains usage data; the bundled provider adapters do not currently supply it:
269
+
270
+ ```python
271
+ from ask_ai import Observability, OpenAI
272
+
273
+ ai = OpenAI().use(Observability(on_event=print))
274
+ response = ai.ask("Explain tracing")
275
+ print(response.metadata.latency_ms)
276
+ ```
277
+
278
+ ### Provider Capabilities
279
+
280
+ Inspect advisory capability metadata before selecting a provider:
281
+
282
+ ```python
283
+ from ask_ai import OpenAI
284
+
285
+ capabilities = OpenAI().capabilities()
286
+ if capabilities.structured_output:
287
+ print("Structured output is available")
288
+ ```
289
+
290
+ ### Smart Router
291
+
292
+ Route requests across providers using deterministic `cheapest`, `fastest`, or
293
+ `most_reliable` policies. Supply static `profiles` for meaningful ordering:
294
+
295
+ ```python
296
+ from ask_ai import Groq, OpenAI, Router
297
+
298
+ openai = OpenAI() # Requires OPENAI_API_KEY
299
+ groq = Groq() # Requires GROQ_API_KEY
300
+ router = Router(
301
+ [openai, groq],
302
+ policy="fastest",
303
+ profiles={
304
+ "OpenAIProvider": {"latency_ms": 100},
305
+ "GroqProvider": {"latency_ms": 50},
306
+ },
307
+ )
308
+ response = router.ask("Explain routing")
309
+ ```
310
+
311
+ ### 🔒 Secure Media Downloads
312
+
313
+ Provider media downloads can be constrained by HTTPS, host, detected MIME, size,
314
+ and timeout policy. DNS results resolving to private, loopback, link-local, or
315
+ reserved IP addresses are rejected by default. `allowed_hosts` is optional;
316
+ set it for an explicit hostname allowlist. Only opt in to `allow_private=True`
317
+ when downloading from trusted private-network hosts. Redirects are rejected:
318
+
319
+ ```python
320
+ from ask_ai import MediaPolicy, download_media
321
+
322
+ data = download_media(
323
+ "https://example.com/image.png",
324
+ MediaPolicy(
325
+ max_bytes=10 * 1024 * 1024,
326
+ allowed_mime_types={"image/png"},
327
+ allowed_hosts={"example.com"},
328
+ ),
329
+ )
330
+ ```
331
+
332
+ ### Conversation Sessions
333
+
334
+ Create optional in-memory conversation state without changing the normal
335
+ provider API:
336
+
337
+ ```python
338
+ session = OpenAI().session(max_messages=20)
339
+ session.ask("My name is Ada.")
340
+ print(session.ask("What is my name?").text)
341
+ ```
342
+
343
+ Session requests do not support `response_model` validation yet.
344
+
224
345
  ---
225
346
 
226
347
  ## 🗺️ Roadmap
227
348
 
228
349
  ### 🚀 Roadmap 2.0 (Active)
229
350
  - [x] Provider Fallback chaining & Pydantic Structured Output support
230
- - [ ] Multi-Modal Vision input (image to text) for all key providers
351
+ - [x] Multi-Modal Vision input (image to text) for all key providers
231
352
  - [ ] Native support for tools/function calling execution
232
- - [ ] Memory buffer / session-based conversation management
353
+ - [x] Optional in-memory session-based conversation management
233
354
 
234
355
  ### 🏁 Roadmap 1.0 (Completed)
235
356
  - ~~[x] OpenAI, Groq, Gemini, Anthropic, Azure, OpenRouter support~~
@@ -0,0 +1,79 @@
1
+ """
2
+ ask-ai: A minimal Python SDK to switch between LLM providers in one line.
3
+ """
4
+
5
+ __version__ = "0.5.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
+ ProviderError,
21
+ ProviderInvalidRequestError,
22
+ ProviderRateLimitError,
23
+ ProviderTimeoutError,
24
+ ProviderUnavailableError,
25
+ StreamNotSupportedError,
26
+ StructuredValidationError,
27
+ )
28
+ from .media import AudioObject, ImageObject
29
+ from .media_security import MediaPolicy, download_media
30
+ from .observability import Observability, ResponseMetadata, Usage
31
+ from .providers import Anthropic, Azure, Google, Groq, OpenAI, OpenRouter
32
+ from .router import Router
33
+ from .session import ConversationSession
34
+ from .structured import parse_structured
35
+
36
+ __all__ = [
37
+ # Providers
38
+ "OpenAI",
39
+ "Anthropic",
40
+ "Google",
41
+ "Groq",
42
+ "Azure",
43
+ "OpenRouter",
44
+ # Core
45
+ "AdvancedConfig",
46
+ "Response",
47
+ "ImageObject",
48
+ "AudioObject",
49
+ # Exceptions
50
+ "AskAIError",
51
+ "AskAIConfigError",
52
+ "AskAINetworkError",
53
+ "AskAIRateLimitError",
54
+ "AskAIParsingError",
55
+ "ProviderError",
56
+ "ProviderAuthError",
57
+ "ProviderRateLimitError",
58
+ "ProviderTimeoutError",
59
+ "ProviderInvalidRequestError",
60
+ "ProviderUnavailableError",
61
+ "MediaTypeNotSupportedError",
62
+ "MediaSecurityError",
63
+ "StreamNotSupportedError",
64
+ "StructuredValidationError",
65
+ "parse_structured",
66
+ "TextDelta",
67
+ "ToolCall",
68
+ "UsageUpdate",
69
+ "Completed",
70
+ "Error",
71
+ "Observability",
72
+ "ResponseMetadata",
73
+ "Usage",
74
+ "Capabilities",
75
+ "Router",
76
+ "ConversationSession",
77
+ "MediaPolicy",
78
+ "download_media",
79
+ ]