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.
- askai_python-0.6.1/PKG-INFO +190 -0
- askai_python-0.6.1/README.md +317 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/__init__.py +3 -2
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/base.py +477 -58
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/config.py +2 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/exceptions.py +12 -7
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/media.py +4 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/media_security.py +53 -11
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/__init__.py +10 -1
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/anthropic_provider.py +7 -1
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/azure_provider.py +1 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/google_provider.py +51 -21
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/groq_provider.py +54 -32
- askai_python-0.6.1/ask_ai/providers/openai_compatible_provider.py +139 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/openai_provider.py +55 -33
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/providers/openrouter_provider.py +1 -0
- askai_python-0.6.1/ask_ai/router.py +183 -0
- askai_python-0.6.1/ask_ai/session.py +159 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/structured.py +116 -8
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/utils.py +1 -2
- askai_python-0.6.1/askai_python.egg-info/PKG-INFO +190 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/SOURCES.txt +3 -0
- askai_python-0.6.1/docs/PYPI_README.md +139 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/pyproject.toml +1 -1
- askai_python-0.6.1/tests/test_capabilities.py +42 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_core.py +523 -10
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_media_security.py +127 -4
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_observability.py +37 -2
- askai_python-0.6.1/tests/test_openai_compatible.py +175 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_options_validation.py +19 -0
- askai_python-0.6.1/tests/test_provider_contract.py +757 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_release_docs.py +5 -5
- askai_python-0.6.1/tests/test_router.py +118 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_session.py +79 -11
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_streaming_events.py +3 -2
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_structured.py +46 -3
- askai_python-0.6.0/PKG-INFO +0 -253
- askai_python-0.6.0/README.md +0 -422
- askai_python-0.6.0/ask_ai/router.py +0 -107
- askai_python-0.6.0/ask_ai/session.py +0 -140
- askai_python-0.6.0/askai_python.egg-info/PKG-INFO +0 -253
- askai_python-0.6.0/docs/PYPI_README.md +0 -202
- askai_python-0.6.0/tests/test_capabilities.py +0 -17
- askai_python-0.6.0/tests/test_router.py +0 -60
- {askai_python-0.6.0 → askai_python-0.6.1}/LICENSE +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/__main__.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/capabilities.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/cli.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/events.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/observability.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/py.typed +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/ask_ai/requirements.txt +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/dependency_links.txt +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/entry_points.txt +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/requires.txt +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/askai_python.egg-info/top_level.txt +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/setup.cfg +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_gitignore.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_google_genai.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_native_structured.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_openrouter_release.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_packaging.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_python39_compat.py +0 -0
- {askai_python-0.6.0 → askai_python-0.6.1}/tests/test_smoke_real.py +0 -0
- {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
|
+
[](https://pypi.org/project/askai-python/)
|
|
55
|
+
[](https://pypi.org/project/askai-python/)
|
|
56
|
+
[](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
|
+
[](https://pypi.org/project/askai-python/)
|
|
4
|
+
[](https://pypi.org/project/askai-python/)
|
|
5
|
+
[](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.
|
|
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",
|