askai-python 0.6.0__tar.gz → 0.6.1__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.6.1/PKG-INFO +190 -0
  2. askai_python-0.6.1/README.md +317 -0
  3. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/__init__.py +3 -2
  4. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/base.py +477 -58
  5. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/config.py +2 -0
  6. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/exceptions.py +12 -7
  7. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/media.py +4 -0
  8. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/media_security.py +53 -11
  9. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/__init__.py +10 -1
  10. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/anthropic_provider.py +7 -1
  11. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/azure_provider.py +1 -0
  12. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/google_provider.py +51 -21
  13. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/groq_provider.py +54 -32
  14. askai_python-0.6.1/ask_ai/providers/openai_compatible_provider.py +139 -0
  15. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/openai_provider.py +55 -33
  16. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/openrouter_provider.py +1 -0
  17. askai_python-0.6.1/ask_ai/router.py +183 -0
  18. askai_python-0.6.1/ask_ai/session.py +159 -0
  19. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/structured.py +116 -8
  20. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/utils.py +1 -2
  21. askai_python-0.6.1/askai_python.egg-info/PKG-INFO +190 -0
  22. {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/SOURCES.txt +3 -0
  23. askai_python-0.6.1/docs/PYPI_README.md +139 -0
  24. {askai_python-0.6.0 → askai_python-0.6.1}/pyproject.toml +1 -1
  25. askai_python-0.6.1/tests/test_capabilities.py +42 -0
  26. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_core.py +523 -10
  27. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_media_security.py +127 -4
  28. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_observability.py +37 -2
  29. askai_python-0.6.1/tests/test_openai_compatible.py +175 -0
  30. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_options_validation.py +19 -0
  31. askai_python-0.6.1/tests/test_provider_contract.py +757 -0
  32. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_release_docs.py +5 -5
  33. askai_python-0.6.1/tests/test_router.py +118 -0
  34. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_session.py +79 -11
  35. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_streaming_events.py +3 -2
  36. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_structured.py +46 -3
  37. askai_python-0.6.0/PKG-INFO +0 -253
  38. askai_python-0.6.0/README.md +0 -422
  39. askai_python-0.6.0/ask_ai/router.py +0 -107
  40. askai_python-0.6.0/ask_ai/session.py +0 -140
  41. askai_python-0.6.0/askai_python.egg-info/PKG-INFO +0 -253
  42. askai_python-0.6.0/docs/PYPI_README.md +0 -202
  43. askai_python-0.6.0/tests/test_capabilities.py +0 -17
  44. askai_python-0.6.0/tests/test_router.py +0 -60
  45. {askai_python-0.6.0 → askai_python-0.6.1}/LICENSE +0 -0
  46. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/__main__.py +0 -0
  47. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/capabilities.py +0 -0
  48. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/cli.py +0 -0
  49. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/events.py +0 -0
  50. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/observability.py +0 -0
  51. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/py.typed +0 -0
  52. {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/requirements.txt +0 -0
  53. {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/dependency_links.txt +0 -0
  54. {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/entry_points.txt +0 -0
  55. {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/requires.txt +0 -0
  56. {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/top_level.txt +0 -0
  57. {askai_python-0.6.0 → askai_python-0.6.1}/setup.cfg +0 -0
  58. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_gitignore.py +0 -0
  59. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_google_genai.py +0 -0
  60. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_native_structured.py +0 -0
  61. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_openrouter_release.py +0 -0
  62. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_packaging.py +0 -0
  63. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_python39_compat.py +0 -0
  64. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_smoke_real.py +0 -0
  65. {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_usage.py +0 -0
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: askai-python
3
+ Version: 0.6.1
4
+ Summary: AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, Anthropic, and more.
5
+ Author-email: Hossein Ghorbani <hosseingh1068@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Hosseinghorbani0/ask-ai
8
+ Project-URL: Documentation, https://github.com/Hosseinghorbani0/ask-ai#readme
9
+ Project-URL: Bug Tracker, https://github.com/Hosseinghorbani0/ask-ai/issues
10
+ Project-URL: Changelog, https://github.com/Hosseinghorbani0/ask-ai/releases
11
+ Keywords: ai,llm,openai,anthropic,gemini,groq,sdk,api
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: openai
27
+ Requires-Dist: openai>=1.0.0; extra == "openai"
28
+ Provides-Extra: anthropic
29
+ Requires-Dist: anthropic>=1.0.0; extra == "anthropic"
30
+ Provides-Extra: google
31
+ Requires-Dist: google-genai>=1.0.0; extra == "google"
32
+ Provides-Extra: groq
33
+ Requires-Dist: groq>=0.4.0; extra == "groq"
34
+ Provides-Extra: media
35
+ Requires-Dist: Pillow>=9.0.0; extra == "media"
36
+ Provides-Extra: httpx
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"
41
+ Provides-Extra: all
42
+ Requires-Dist: openai>=1.0.0; extra == "all"
43
+ Requires-Dist: anthropic>=1.0.0; extra == "all"
44
+ Requires-Dist: google-genai>=1.0.0; extra == "all"
45
+ Requires-Dist: groq>=0.4.0; extra == "all"
46
+ Requires-Dist: Pillow>=9.0.0; extra == "all"
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"
50
+ Dynamic: license-file
51
+
52
+ # ask-ai
53
+
54
+ [![PyPI version](https://img.shields.io/pypi/v/askai-python.svg)](https://pypi.org/project/askai-python/)
55
+ [![Python versions](https://img.shields.io/pypi/pyversions/askai-python.svg)](https://pypi.org/project/askai-python/)
56
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
57
+
58
+ **A small, typed Python SDK for calling several LLM providers through a consistent API.**
59
+ Use it from scripts and Python applications without adopting a larger orchestration framework.
60
+
61
+ `ask-ai` is a client library, not a hosted service or gateway. Create a provider client, call it synchronously or asynchronously, and receive a common `Response` object. Provider SDKs are optional; install only the ones you use.
62
+
63
+ - [Full documentation and language versions](https://github.com/Hosseinghorbani0/ask-ai#readme)
64
+ - [Report an issue](https://github.com/Hosseinghorbani0/ask-ai/issues)
65
+
66
+ ## Features
67
+
68
+ - Sync, async, and streaming text request APIs.
69
+ - Provider fallback and policy-based routing.
70
+ - Retries for transient failures, individual-call timeouts, and total deadlines.
71
+ - JSON and validated structured responses for supported providers.
72
+ - Typed response metadata, normalized token usage, and optional observability hooks.
73
+ - Optional in-memory conversation sessions.
74
+ - OpenAI-compatible Chat Completions endpoints, including local servers.
75
+ - Policy-controlled secure media downloads.
76
+
77
+ The public request API currently sends text; vision input and persistent session storage are not provided. Tool-call execution is not provided.
78
+
79
+ ## Requirements and installation
80
+
81
+ Python 3.10 or newer is required. Core dependencies are empty; provider SDKs are optional extras.
82
+
83
+ ```bash
84
+ python -m pip install askai-python
85
+ python -m pip install "askai-python[openai]" # OpenAI, Azure OpenAI, OpenRouter, compatible endpoints
86
+ python -m pip install "askai-python[anthropic]"
87
+ python -m pip install "askai-python[google]"
88
+ python -m pip install "askai-python[groq]"
89
+ python -m pip install "askai-python[structured]" # Pydantic and jsonschema
90
+ python -m pip install "askai-python[media]" # Pillow
91
+ python -m pip install "askai-python[httpx]" # secure media downloads
92
+ python -m pip install "askai-python[all]"
93
+ ```
94
+
95
+ Set the provider credential in an environment variable or pass `api_key=` to its constructor. Never commit credentials to source control.
96
+
97
+ ## Providers
98
+
99
+ | Provider | Import | Extra | Environment variable | Default model |
100
+ |---|---|---|---|---|
101
+ | OpenAI | `OpenAI` | `openai` | `OPENAI_API_KEY` | `gpt-4o` |
102
+ | Anthropic | `Anthropic` | `anthropic` | `ANTHROPIC_API_KEY` | `claude-sonnet-4-20250514` |
103
+ | Google Gen AI | `Google` | `google` | `GOOGLE_API_KEY` | `gemini-2.0-flash` |
104
+ | Groq | `Groq` | `groq` | `GROQ_API_KEY` | `llama-3.3-70b-versatile` |
105
+ | Azure OpenAI | `Azure` | `openai` | `AZURE_OPENAI_API_KEY` | Deployment required |
106
+ | OpenRouter | `OpenRouter` | `openai` | `OPENROUTER_API_KEY` | `openai/gpt-4o` |
107
+ | OpenAI-compatible | `OpenAICompatibleProvider` | `openai` | None (optional) | Required |
108
+
109
+ Azure also needs `AZURE_OPENAI_ENDPOINT`. Its deployment can be supplied as `model=` or through `AZURE_OPENAI_DEPLOYMENT_NAME`; `AZURE_OPENAI_API_VERSION` defaults to `2024-06-01`. The compatible provider requires an absolute HTTP(S) `base_url` and a model; `api_key` is optional for local servers.
110
+
111
+ `capabilities()` describes declared provider/model features. It does not guarantee availability for a particular model or account. In particular, vision input and tool-call execution are not general request features.
112
+
113
+ ## Quick start and API
114
+
115
+ ```python
116
+ from ask_ai import OpenAI
117
+
118
+ client = OpenAI() # Reads OPENAI_API_KEY
119
+ response = client.ask("Explain what a Python context manager does.")
120
+ print(response.text)
121
+ ```
122
+
123
+ The public methods are `ask()` (sync), `ask_async()` (async), `ask_stream()` (sync text iterator), `ask_stream_async()` (async text iterator), and their typed event variants `ask_stream_events()` and `ask_stream_events_async()`.
124
+
125
+ `Response` exposes `text`, unmodified `raw`, parsed `json`, `parsed`/`pydantic` for validated structured results, optional `media`, `usage`, and `metadata`.
126
+
127
+ ## Request options and reliability
128
+
129
+ Set options per request or via `provider.advanced(...)`. Common options include `temperature`, `max_tokens`, `top_p`, `frequency_penalty`, `presence_penalty`, `stop`, `system_message` (also `system` or `prompt`), `timeout`, `total_timeout`, `retry`, `json`, `response_model`, `validation_retries`, `output_type`, `clean`, `strip`, `code`, and `strict_options`.
130
+
131
+ Unknown options warn by default; `strict_options=True` raises `ValueError`. Provider support for individual options varies. `timeout` limits an individual provider call; `total_timeout` bounds the complete operation. Retry defaults to 2 additional attempts for transient failures, honors `Retry-After` with a 30-second clamp, and does not retry non-retryable errors. Retry/fallback requests can incur additional charges. A stream is not retried after chunks have been yielded.
132
+
133
+ Fallback can be requested with `providers=[OpenAI, Groq]`. `Router` provides policy-based selection and a `max_attempts` budget. See the [full guide](https://github.com/Hosseinghorbani0/ask-ai#retries-fallback-and-routing) for examples.
134
+
135
+ ## Structured output
136
+
137
+ Supported schemas include Pydantic models, dataclasses, `TypedDict`, and JSON Schema. Validation happens locally; access the result through `response.parsed`. Provider-native strict schema behavior differs. Structured validation is not available on session streaming methods.
138
+
139
+ ```python
140
+ from pydantic import BaseModel
141
+ from ask_ai import OpenAI
142
+
143
+ class Summary(BaseModel):
144
+ title: str
145
+ points: list[str]
146
+
147
+ response = OpenAI().ask(
148
+ "Summarize the water cycle.",
149
+ response_model=Summary,
150
+ validation_retries=1,
151
+ )
152
+ print(response.parsed.title)
153
+ ```
154
+
155
+ ## Async, media, sessions, and lifecycle
156
+
157
+ ```python
158
+ import asyncio
159
+ from ask_ai import OpenAI
160
+
161
+ async def main():
162
+ async with OpenAI() as client:
163
+ response = await client.ask_async("Define an API.")
164
+ print(response.text)
165
+ async for chunk in client.ask_stream_async("Say it in five words."):
166
+ print(chunk, end="", flush=True)
167
+
168
+ asyncio.run(main())
169
+ ```
170
+
171
+ Some providers implement generated image/audio responses using `output_type="image"` or `"audio"`; availability varies by provider and model. Image input for vision is not currently exposed by the request API. Secure media downloads require the `httpx` extra; downloads require HTTPS, reject redirects and non-public DNS answers by default, and enforce a 25 MiB limit and 30-second timeout. `MediaPolicy` can further restrict allowed hosts and MIME types.
172
+
173
+ `provider.session()` provides optional in-memory conversation history; it is not persisted. Providers support `close()`/`aclose()` and sync/async context managers. An async client belongs to the event loop that first uses it; create one provider per loop.
174
+
175
+ ## Observability and troubleshooting
176
+
177
+ `provider.use(Observability(on_event=...))` receives request metadata, latency, retries, and reported token usage. Estimated cost requires supplied pricing rates and provider usage. Do not send secrets or sensitive prompts to custom telemetry.
178
+
179
+ - Install the matching extra if a provider SDK is missing.
180
+ - Set the provider's environment key or pass `api_key=`.
181
+ - For Azure, configure the endpoint and deployment as well as credentials.
182
+ - Construct a fresh provider if an async event-loop ownership error occurs.
183
+ - Confirm model and option support with the provider; mock tests cannot guarantee live API behavior.
184
+ - Live integration calls may incur charges.
185
+
186
+ Python 3.10–3.13 are listed in package classifiers. The package includes `ask_ai/py.typed`.
187
+
188
+ ## License and support
189
+
190
+ Distributed under the [MIT License](https://github.com/Hosseinghorbani0/ask-ai/blob/main/LICENSE). For bug reports and feature requests, use the [GitHub issue tracker](https://github.com/Hosseinghorbani0/ask-ai/issues). Please include a minimal reproducible example and remove API keys, personal information, and other secrets.
@@ -0,0 +1,317 @@
1
+ # ask-ai
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/askai-python.svg)](https://pypi.org/project/askai-python/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/askai-python.svg)](https://pypi.org/project/askai-python/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ **A small, typed Python SDK for calling several LLM providers through a consistent API.**
8
+ Use it from scripts and Python applications without adopting a larger orchestration framework.
9
+
10
+ `ask-ai` is a client library, not a hosted service or gateway. You create a provider client, call it synchronously or asynchronously, and receive a common `Response` object. Provider SDKs are optional; install only the ones you use.
11
+
12
+ ## Documentation languages
13
+
14
+ [English](README.md) · [العربية](docs/README_ar.md) · [فارسی](docs/README_fa.md) · [Español](docs/README_es.md) · [日本語](docs/README_ja.md) · [Русский](docs/README_ru.md) · [Türkçe](docs/README_tr.md) · [中文](docs/README_zh.md)
15
+
16
+ ## Contents
17
+
18
+ - [Features](#features)
19
+ - [Requirements and installation](#requirements-and-installation)
20
+ - [Providers](#providers)
21
+ - [Quick start](#quick-start)
22
+ - [Public API](#public-api)
23
+ - [Request options](#request-options)
24
+ - [Retries, fallback, and routing](#retries-fallback-and-routing)
25
+ - [Structured output](#structured-output)
26
+ - [Streaming and asynchronous use](#streaming-and-asynchronous-use)
27
+ - [Media generation and secure downloads](#media-generation-and-secure-downloads)
28
+ - [Conversation sessions and client lifecycle](#conversation-sessions-and-client-lifecycle)
29
+ - [Capabilities and observability](#capabilities-and-observability)
30
+ - [Architecture](#architecture)
31
+ - [CLI](#command-line-interface)
32
+ - [Compatibility and troubleshooting](#compatibility-and-troubleshooting)
33
+ - [Contributing, support, and license](#contributing-support-and-license)
34
+
35
+ ## Features
36
+
37
+ - Consistent `ask`, `ask_async`, `ask_stream`, and `ask_stream_async` APIs.
38
+ - Optional provider fallback and policy-based routing.
39
+ - Retry handling for transient failures, with per-call and total deadlines.
40
+ - Typed response metadata, normalized token usage, and optional observability hooks.
41
+ - JSON and validated structured responses for supported providers.
42
+ - Optional in-memory conversation sessions.
43
+ - OpenAI-compatible Chat Completions endpoints, including local servers.
44
+ - Secure, policy-controlled media downloads.
45
+
46
+ The public request API currently sends text; provider capability metadata does not imply vision input support. Tool-call execution and persistent conversation storage are not provided.
47
+
48
+ ## Requirements and installation
49
+
50
+ - Python 3.10+ is required.
51
+ - Core package dependencies are empty. Provider SDKs and media/structured-output libraries are optional extras.
52
+
53
+ Install the base package:
54
+
55
+ ```bash
56
+ python -m pip install askai-python
57
+ ```
58
+
59
+ Install only the provider SDKs you need:
60
+
61
+ ```bash
62
+ python -m pip install "askai-python[openai,groq]" # OpenAI and Groq
63
+ python -m pip install "askai-python[openai]" # OpenAI, Azure OpenAI, OpenRouter, compatible endpoints
64
+ python -m pip install "askai-python[anthropic]"
65
+ python -m pip install "askai-python[google]"
66
+ python -m pip install "askai-python[groq]"
67
+ python -m pip install "askai-python[structured]" # Pydantic and jsonschema
68
+ python -m pip install "askai-python[media]" # Pillow
69
+ python -m pip install "askai-python[httpx]" # secure media downloads
70
+ python -m pip install "askai-python[all]"
71
+ ```
72
+
73
+ Set credentials in your environment or pass `api_key=` when constructing a provider. Avoid hard-coding secrets in source code.
74
+
75
+ ## Providers
76
+
77
+ | Provider | Import | Extra | Credential environment variable | Default model |
78
+ |---|---|---|---|---|
79
+ | OpenAI | `OpenAI` | `openai` | `OPENAI_API_KEY` | `gpt-4o` |
80
+ | Anthropic | `Anthropic` | `anthropic` | `ANTHROPIC_API_KEY` | `claude-sonnet-4-20250514` |
81
+ | Google Gen AI | `Google` | `google` | `GOOGLE_API_KEY` | `gemini-2.0-flash` |
82
+ | Groq | `Groq` | `groq` | `GROQ_API_KEY` | `llama-3.3-70b-versatile` |
83
+ | Azure OpenAI | `Azure` | `openai` | `AZURE_OPENAI_API_KEY` | Deployment name required |
84
+ | OpenRouter | `OpenRouter` | `openai` | `OPENROUTER_API_KEY` | `openai/gpt-4o` |
85
+ | OpenAI-compatible | `OpenAICompatibleProvider` | `openai` | None (optional) | Required at construction |
86
+
87
+ Azure also requires `AZURE_OPENAI_ENDPOINT`; its deployment can be provided as `model=` or through `AZURE_OPENAI_DEPLOYMENT_NAME`. `AZURE_OPENAI_API_VERSION` defaults to `2024-06-01`. The compatible provider requires an absolute HTTP(S) `base_url` and a model; `api_key` may be omitted for local endpoints.
88
+
89
+ `capabilities()` returns declared support for text, streaming, async streaming, structured output, tool calls, image generation, audio generation, and vision. These are provider declarations, not a guarantee that every model or account enables the feature. In particular, tool-call execution and vision input are not exposed as general request features.
90
+
91
+ ## Quick start
92
+
93
+ ```python
94
+ from ask_ai import OpenAI
95
+
96
+ client = OpenAI() # Reads OPENAI_API_KEY
97
+ response = client.ask("Explain what a Python context manager does.")
98
+ print(response.text)
99
+ ```
100
+
101
+ Switch providers by using a different provider class:
102
+
103
+ ```python
104
+ from ask_ai import Anthropic, Google, Groq
105
+
106
+ answer = Groq().ask("Give me one useful shell tip.")
107
+ print(answer.text)
108
+ ```
109
+
110
+ ## Public API
111
+
112
+ | Method | Result | Description |
113
+ |---|---|---|
114
+ | `provider.ask(query, **options)` | `Response` | Synchronous request |
115
+ | `await provider.ask_async(query, **options)` | `Response` | Native asynchronous request |
116
+ | `provider.ask_stream(query, **options)` | Iterator of strings | Synchronous text stream |
117
+ | `async for chunk in provider.ask_stream_async(query, **options)` | Async iterator of strings | Asynchronous text stream |
118
+ | `provider.ask_stream_events(query, **options)` | Iterator of event objects | Typed synchronous stream |
119
+ | `provider.ask_stream_events_async(query, **options)` | Async iterator of event objects | Typed asynchronous stream |
120
+
121
+ The `Response` provides `text`, unmodified `raw`, parsed `json`, `parsed`/`pydantic` when `response_model` was requested, optional `media`, `usage`, and `metadata`. `response.json` parses the response text; use `json=True` to request JSON-style output, or `response_model=...` when validation is required.
122
+
123
+ ## Request options
124
+
125
+ Pass options to a request or set defaults using `provider.advanced(...)`. Options are merged, with request options taking precedence.
126
+
127
+ | Option | Purpose |
128
+ |---|---|
129
+ | `temperature`, `max_tokens`, `top_p` | Common generation controls |
130
+ | `frequency_penalty`, `presence_penalty`, `stop` | Provider-supported sampling controls |
131
+ | `system_message` (aliases: `system`, `prompt`) | System instruction |
132
+ | `timeout` | Timeout for an individual provider call (default 30 seconds in provider clients) |
133
+ | `total_timeout` | Deadline for the complete operation, including retries |
134
+ | `retry` | Additional retry count; defaults to 2 (up to 3 attempts) |
135
+ | `json=True` | Request JSON-formatted output and allow parsing via `response.json` |
136
+ | `response_model`, `validation_retries` | Parse and validate a supported structured model |
137
+ | `output_type` | Request supported generated `image` or `audio` output |
138
+ | `clean`, `strip`, `code` | Apply text cleanup/extraction when reading `response.text` |
139
+ | `strict_options=True` | Raise on unknown options instead of warning |
140
+
141
+ Provider/model support for individual options varies. Unknown options warn by default; strict mode raises `ValueError`. The deprecated `safe_mode` option has no defined behavior. Unsupported output types raise rather than being silently ignored.
142
+
143
+ ```python
144
+ response = client.ask(
145
+ "Summarize this topic in three bullets.",
146
+ temperature=0.2,
147
+ max_tokens=250,
148
+ system_message="Be concise and factual.",
149
+ total_timeout=20,
150
+ )
151
+ ```
152
+
153
+ ## Retries, fallback, and routing
154
+
155
+ Requests default to two retries for retryable transient failures (up to three attempts). Invalid requests, authentication failures, and other non-retryable errors are not retried. `Retry-After` is honored within a 30-second clamp. `timeout` limits an individual call; `total_timeout` bounds the complete retry operation.
156
+
157
+ Fallback can try provider classes or instances:
158
+
159
+ ```python
160
+ from ask_ai import OpenAI, Groq
161
+
162
+ response = OpenAI().ask("Hello", providers=[OpenAI, Groq], retry=1)
163
+ ```
164
+
165
+ The fallback chain advances only for retryable provider failures. `Router` provides policy-based selection and an overall `max_attempts` budget:
166
+
167
+ ```python
168
+ from ask_ai import OpenAI, Groq, Router
169
+
170
+ router = Router([OpenAI(), Groq()], max_attempts=2)
171
+ response = router.ask("Hello", retry=1)
172
+ ```
173
+
174
+ Retries and fallbacks can multiply requests and associated provider charges. Set a small retry count and a total deadline for cost- and latency-sensitive work. A stream is not retried after chunks have already been yielded.
175
+
176
+ ## Structured output
177
+
178
+ Use `response_model` with supported Pydantic models, dataclasses, `TypedDict`, or JSON Schema definitions. The request returns a regular `Response`; access the validated result through `.parsed`:
179
+
180
+ ```python
181
+ from pydantic import BaseModel
182
+ from ask_ai import OpenAI
183
+
184
+ class Summary(BaseModel):
185
+ title: str
186
+ points: list[str]
187
+
188
+ response = OpenAI().ask(
189
+ "Summarize the water cycle.",
190
+ response_model=Summary,
191
+ validation_retries=1,
192
+ )
193
+ summary = response.parsed
194
+ print(summary.title)
195
+ ```
196
+
197
+ Provider-side structured-output support differs. Validation is performed locally, and unsupported or invalid output raises a structured validation error rather than guaranteeing a provider-specific strict schema mode. Structured validation is not supported by session streaming methods.
198
+
199
+ ## Streaming and asynchronous use
200
+
201
+ ```python
202
+ for chunk in client.ask_stream("Write a short greeting."):
203
+ print(chunk, end="", flush=True)
204
+ ```
205
+
206
+ Async methods use provider async clients:
207
+
208
+ ```python
209
+ import asyncio
210
+ from ask_ai import OpenAI
211
+
212
+ async def main():
213
+ client = OpenAI()
214
+ response = await client.ask_async("Give me a concise definition of an API.")
215
+ print(response.text)
216
+ async for chunk in client.ask_stream_async("Now say it in five words."):
217
+ print(chunk, end="", flush=True)
218
+ await client.aclose()
219
+
220
+ asyncio.run(main())
221
+ ```
222
+
223
+ Typed stream events include `TextDelta`, `UsageUpdate`, `Completed`, and `Error`. `ToolCall` is exported as an event type, but current provider adapters do not emit tool-call events. Use `ask_stream_events` or `ask_stream_events_async` when event objects are preferable to text chunks. Catch cancellation and provider exceptions in the calling application as appropriate.
224
+
225
+ ## Media generation and secure downloads
226
+
227
+ Some provider implementations support generated image or audio output through `output_type="image"` or `output_type="audio"`. Availability depends on provider and model; inspect `capabilities()` and provider documentation. The request API does not currently accept image input for vision.
228
+
229
+ Secure media downloads require the optional `httpx` extra. By default, downloads require HTTPS, reject redirects and non-public DNS results, and enforce a 25 MiB limit and 30-second timeout. You may restrict MIME types and hosts:
230
+
231
+ ```python
232
+ from ask_ai import MediaPolicy, download_media
233
+
234
+ data = download_media(
235
+ "https://example.com/image.png",
236
+ MediaPolicy(
237
+ max_bytes=10 * 1024 * 1024,
238
+ allowed_mime_types={"image/png"},
239
+ allowed_hosts={"example.com"},
240
+ ),
241
+ )
242
+ ```
243
+
244
+ Only set `allow_private=True` for trusted private-network URLs. A media policy does not make arbitrary provider-generated URLs safe to use elsewhere.
245
+
246
+ ## Conversation sessions and client lifecycle
247
+
248
+ `provider.session()` creates an optional in-memory conversation:
249
+
250
+ ```python
251
+ with OpenAI() as client:
252
+ session = client.session(max_messages=20)
253
+ session.ask("My name is Ada.")
254
+ print(session.ask("What is my name?").text)
255
+ ```
256
+
257
+ History is held in process memory and is not persisted. `max_messages` bounds retained messages by user/assistant pairs. Concurrent sync operations serialize separately from async operations; do not mix sync and async operations concurrently on one session.
258
+
259
+ Providers support `close()`/`aclose()` and synchronous/asynchronous context managers. Close clients when finished, especially in long-running services. A cached async client is bound to the first event loop that uses it; use a provider instance per loop.
260
+
261
+ ## Capabilities and observability
262
+
263
+ Use `provider.capabilities()` to inspect declared provider support. Attach an observability hook with `provider.use(Observability(...))` to receive request metadata such as provider, model, latency, retry count, and normalized token usage. Estimated cost is available only when pricing rates are supplied and usage is reported by the provider.
264
+
265
+ ```python
266
+ from ask_ai import Observability, OpenAI
267
+
268
+ events = []
269
+ client = OpenAI()
270
+ client.use(Observability(on_event=events.append))
271
+ response = client.ask("Return one word.")
272
+ print(response.metadata)
273
+ ```
274
+
275
+ Avoid putting prompts, personal data, or credentials into your own telemetry handlers. Hook exceptions are isolated from the request path.
276
+
277
+ ## Architecture
278
+
279
+ `ask_ai` provides a shared provider base, provider-specific SDK adapters, a common `Response`, optional `Router` and `ConversationSession`, and utilities for structured output and media security. Provider SDKs are imported optionally, so importing the package does not require installing every integration. The library does not proxy requests through its own service.
280
+
281
+ ## Command-line interface
282
+
283
+ The `ask-ai` command is installed with the package. It supports OpenAI, Groq, Google, OpenRouter, Azure, and Anthropic:
284
+
285
+ ```bash
286
+ ask-ai --provider openai "Explain recursion in one sentence."
287
+ ask-ai --provider groq --stream "List three Python tips."
288
+ ask-ai --help
289
+ ```
290
+
291
+ The CLI uses the selected provider's environment credentials. The generic OpenAI-compatible provider is available through the Python API, not the current CLI provider choices.
292
+
293
+ ## Compatibility and troubleshooting
294
+
295
+ - **Python:** 3.10–3.13 are listed in package classifiers; the declared minimum is 3.10.
296
+ - **Typing:** the distribution includes `ask_ai/py.typed`.
297
+ - **Operating systems:** the package is pure Python; provider SDK support and network behavior depend on your platform and installed SDK.
298
+ - **No SDK installed:** install the matching provider extra, for example `askai-python[openai]`.
299
+ - **Missing credentials:** set the provider environment variable listed above, or pass `api_key=`.
300
+ - **Azure initialization fails:** set `AZURE_OPENAI_ENDPOINT`, API key, and a deployment name/model.
301
+ - **Unknown option warning:** check the spelling, or set `strict_options=True` to fail immediately.
302
+ - **Async loop error:** do not reuse an async provider client across event loops; construct one provider per loop.
303
+ - **Structured output invalid:** validate the model/schema and increase `validation_retries` only when another billed attempt is acceptable.
304
+ - **Live provider behavior:** mock-based tests do not guarantee that a live provider accepts a model or option. Real-provider checks are optional and may incur charges.
305
+
306
+ ## Contributing, support, and license
307
+
308
+ Bug reports and feature requests are welcome via the [issue tracker](https://github.com/Hosseinghorbani0/ask-ai/issues). For code contributions, open a focused pull request with tests and documentation updates for behavior changes. Run the project checks locally:
309
+
310
+ ```bash
311
+ python -m pytest -q
312
+ ruff check .
313
+ ```
314
+
315
+ Review [CHANGELOG.md](CHANGELOG.md) for release history. Usage questions and bug reports should include a minimal reproducible example and omit API keys, personal data, and other secrets.
316
+
317
+ Licensed under the [MIT License](LICENSE). See the [PyPI project page](https://pypi.org/project/askai-python/) and [GitHub repository](https://github.com/Hosseinghorbani0/ask-ai) for releases and project updates.
@@ -2,7 +2,7 @@
2
2
  ask-ai: A minimal Python SDK to switch between LLM providers in one line.
3
3
  """
4
4
 
5
- __version__ = "0.6.0"
5
+ __version__ = "0.6.1"
6
6
 
7
7
  from .base import Response
8
8
  from .capabilities import Capabilities
@@ -29,7 +29,7 @@ from .exceptions import (
29
29
  from .media import AudioObject, ImageObject
30
30
  from .media_security import MediaPolicy, download_media
31
31
  from .observability import Observability, ResponseMetadata, Usage
32
- from .providers import Anthropic, Azure, Google, Groq, OpenAI, OpenRouter
32
+ from .providers import Anthropic, Azure, Google, Groq, OpenAI, OpenAICompatibleProvider, OpenRouter
33
33
  from .router import Router
34
34
  from .session import ConversationSession
35
35
  from .structured import parse_structured
@@ -42,6 +42,7 @@ __all__ = [
42
42
  "Groq",
43
43
  "Azure",
44
44
  "OpenRouter",
45
+ "OpenAICompatibleProvider",
45
46
  # Core
46
47
  "AdvancedConfig",
47
48
  "Response",