askai-python 0.2.0__tar.gz → 0.2.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.2.0/askai_python.egg-info → askai_python-0.2.1}/PKG-INFO +28 -11
- {askai_python-0.2.0 → askai_python-0.2.1}/README.md +26 -12
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/base.py +37 -7
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/exceptions.py +4 -0
- askai_python-0.2.1/ask_ai/utils.py +43 -0
- {askai_python-0.2.0 → askai_python-0.2.1/askai_python.egg-info}/PKG-INFO +28 -11
- {askai_python-0.2.0 → askai_python-0.2.1}/askai_python.egg-info/SOURCES.txt +1 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/docs/PYPI_README.md +27 -10
- {askai_python-0.2.0 → askai_python-0.2.1}/pyproject.toml +1 -1
- {askai_python-0.2.0 → askai_python-0.2.1}/LICENSE +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/__init__.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/__main__.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/cli.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/config.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/media.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/__init__.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/anthropic_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/azure_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/google_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/groq_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/openai_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/openrouter_provider.py +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/askai_python.egg-info/dependency_links.txt +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/askai_python.egg-info/requires.txt +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/askai_python.egg-info/top_level.txt +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/setup.cfg +0 -0
- {askai_python-0.2.0 → askai_python-0.2.1}/tests/test_core.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: askai-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, and more.
|
|
5
5
|
Author-email: Hossein Ghorbani <hosseingh1068@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -53,16 +53,6 @@ print(Groq().ask("Explain black holes like I'm 5").text)
|
|
|
53
53
|
- **Zero config**: Keys are pulled from the environment automatically
|
|
54
54
|
- **SDK-first, not a framework**: It stays out of your way.
|
|
55
55
|
|
|
56
|
-
## ⚖️ How it compares
|
|
57
|
-
|
|
58
|
-
| Feature | ask-ai | LangChain |
|
|
59
|
-
| -------------- | ------ | --------- |
|
|
60
|
-
| Setup time | 30 sec | 1 hour |
|
|
61
|
-
| Learning curve | ⭐ | ⭐⭐⭐⭐⭐ |
|
|
62
|
-
| Async support | ⏳ *(Coming soon)* | ⚠️ Complex |
|
|
63
|
-
| Retry/Timeout | ✅ Built-in | ❌ Manual |
|
|
64
|
-
| Gateway needed | ❌ No | ❌ No |
|
|
65
|
-
|
|
66
56
|
## 🚫 What this project is NOT
|
|
67
57
|
|
|
68
58
|
> ❌ Not an AI framework
|
|
@@ -73,6 +63,33 @@ It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
|
73
63
|
|
|
74
64
|
---
|
|
75
65
|
|
|
66
|
+
## 🛠️ Advanced Usage
|
|
67
|
+
|
|
68
|
+
### 🧰 Developer QoL Utilities (Auto-Parsing)
|
|
69
|
+
Stop writing Regex to clean up model outputs! `ask-ai` comes with built-in text processing flags:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from ask_ai import OpenAI
|
|
73
|
+
ai = OpenAI()
|
|
74
|
+
|
|
75
|
+
# 1. Clean Markdown (Removes ```json and ``` tags)
|
|
76
|
+
# Perfect for extracting raw data from models that wrap everything in markdown
|
|
77
|
+
clean_text = ai.ask("Write JSON", clean=True).text
|
|
78
|
+
|
|
79
|
+
# 2. Extract Code (Returns ONLY the code block, ignores conversational filler)
|
|
80
|
+
# Great for automation pipelines
|
|
81
|
+
code = ai.ask("Write a python ping script", code=True).text
|
|
82
|
+
|
|
83
|
+
# 3. Strip Tags (Removes <think> blocks and HTML)
|
|
84
|
+
# Essential for reasoning models like DeepSeek-R1
|
|
85
|
+
answer_only = ai.ask("What is 1+1?", strip=True).text
|
|
86
|
+
|
|
87
|
+
# 4. Enforce & Parse JSON (Directly returns a Parsed Python Dictionary)
|
|
88
|
+
# Adds JSON instructions to the prompt and safely runs json.loads()
|
|
89
|
+
data_dict = ai.ask("Extract user info", json=True).json
|
|
90
|
+
print(data_dict['name'])
|
|
91
|
+
```
|
|
92
|
+
|
|
76
93
|
## 🚀 Built-in Resiliency (Retries & Timeouts)
|
|
77
94
|
|
|
78
95
|
Build reliable apps without writing your own loops. `askai-python` handles rate limits (`429`) and network drops via an internal exponential backoff.
|
|
@@ -48,17 +48,6 @@ Groq().ask("Explain black holes like I'm 5").text
|
|
|
48
48
|
- **Zero config**: Keys are pulled from the environment automatically
|
|
49
49
|
- **SDK-first, not a framework**: It stays out of your way.
|
|
50
50
|
|
|
51
|
-
## ⚖️ How it compares
|
|
52
|
-
|
|
53
|
-
| Feature | ask-ai | LangChain |
|
|
54
|
-
| -------------- | ------ | --------- |
|
|
55
|
-
| Setup time | 30 sec | 1 hour |
|
|
56
|
-
| Learning curve | ⭐ | ⭐⭐⭐⭐⭐ |
|
|
57
|
-
| Async support | ⏳ *(Coming soon)* | ⚠️ Complex |
|
|
58
|
-
| Retry/Timeout | ✅ Built-in | ❌ Manual |
|
|
59
|
-
| Gateway needed | ❌ No | ❌ No |
|
|
60
|
-
| Lines to switch| **1** | 20+ |
|
|
61
|
-
|
|
62
51
|
## 🚫 What this project is NOT
|
|
63
52
|
|
|
64
53
|
> ❌ Not an AI framework
|
|
@@ -71,7 +60,32 @@ It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
|
71
60
|
|
|
72
61
|
## 🛠️ Advanced Usage
|
|
73
62
|
|
|
74
|
-
###
|
|
63
|
+
### 🧰 Developer QoL Utilities (Auto-Parsing)
|
|
64
|
+
Stop writing Regex to clean up model outputs! `ask-ai` comes with built-in text processing flags:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from ask_ai import OpenAI
|
|
68
|
+
ai = OpenAI()
|
|
69
|
+
|
|
70
|
+
# 1. Clean Markdown (Removes ```json and ``` tags)
|
|
71
|
+
# Perfect for extracting raw data from models that wrap everything in markdown
|
|
72
|
+
clean_text = ai.ask("Write JSON", clean=True).text
|
|
73
|
+
|
|
74
|
+
# 2. Extract Code (Returns ONLY the code block, ignores conversational filler)
|
|
75
|
+
# Great for automation pipelines
|
|
76
|
+
code = ai.ask("Write a python ping script", code=True).text
|
|
77
|
+
|
|
78
|
+
# 3. Strip Tags (Removes <think> blocks and HTML)
|
|
79
|
+
# Essential for reasoning models like DeepSeek-R1
|
|
80
|
+
answer_only = ai.ask("What is 1+1?", strip=True).text
|
|
81
|
+
|
|
82
|
+
# 4. Enforce & Parse JSON (Directly returns a Parsed Python Dictionary)
|
|
83
|
+
# Adds JSON instructions to the prompt and safely runs json.loads()
|
|
84
|
+
data_dict = ai.ask("Extract user info", json=True).json
|
|
85
|
+
print(data_dict['name'])
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 🔄 Built-in Retries & Timeout (Resiliency)
|
|
75
89
|
Handle rate limits (`429`) and network drops automatically:
|
|
76
90
|
|
|
77
91
|
```python
|
|
@@ -9,9 +9,30 @@ class Response:
|
|
|
9
9
|
"""
|
|
10
10
|
Unified response object for all easyai requests.
|
|
11
11
|
"""
|
|
12
|
-
def __init__(self, text: str = "", media: Union[ImageObject, AudioObject, None] = None):
|
|
13
|
-
self.
|
|
12
|
+
def __init__(self, text: str = "", media: Union[ImageObject, AudioObject, None] = None, **kwargs):
|
|
13
|
+
self._raw_text = text
|
|
14
14
|
self.media = media
|
|
15
|
+
self._kwargs = kwargs
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def text(self) -> str:
|
|
19
|
+
text = self._raw_text
|
|
20
|
+
if self._kwargs.get('strip'):
|
|
21
|
+
from .utils import strip_tags
|
|
22
|
+
text = strip_tags(text)
|
|
23
|
+
if self._kwargs.get('clean'):
|
|
24
|
+
from .utils import clean_markdown
|
|
25
|
+
text = clean_markdown(text)
|
|
26
|
+
if self._kwargs.get('code'):
|
|
27
|
+
from .utils import extract_code
|
|
28
|
+
text = extract_code(text)
|
|
29
|
+
return text
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def json(self) -> Union[Dict[str, Any], list]:
|
|
33
|
+
"""Returns parsed JSON. If 'json=True' was passed, this is highly reliable."""
|
|
34
|
+
from .utils import parse_json
|
|
35
|
+
return parse_json(self._raw_text)
|
|
15
36
|
|
|
16
37
|
def __str__(self):
|
|
17
38
|
return self.text
|
|
@@ -96,7 +117,10 @@ class BaseProvider:
|
|
|
96
117
|
last_error = None
|
|
97
118
|
while attempts <= retry:
|
|
98
119
|
try:
|
|
99
|
-
|
|
120
|
+
response = self._send_request(messages, final_config, output_type)
|
|
121
|
+
# Attach parsing arguments to the response object dynamically
|
|
122
|
+
response._kwargs.update(kwargs)
|
|
123
|
+
return response
|
|
100
124
|
except Exception as e:
|
|
101
125
|
last_error = e
|
|
102
126
|
if self._is_retryable_error(e) and attempts < retry:
|
|
@@ -133,10 +157,16 @@ class BaseProvider:
|
|
|
133
157
|
|
|
134
158
|
def _prepare_messages(self, query: str, config: AdvancedConfig) -> List[Dict[str, str]]:
|
|
135
159
|
messages = []
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
160
|
+
|
|
161
|
+
system_msg = config.system_message or self.persona or ""
|
|
162
|
+
|
|
163
|
+
# Smart capability: if json=True, enforce JSON system prompt
|
|
164
|
+
if config.extra.get('json'):
|
|
165
|
+
json_instruction = "You must respond with valid JSON only, without any markdown formatting or tags."
|
|
166
|
+
system_msg = f"{system_msg}\n{json_instruction}".strip()
|
|
167
|
+
|
|
168
|
+
if system_msg:
|
|
169
|
+
messages.append({"role": "system", "content": system_msg})
|
|
140
170
|
|
|
141
171
|
messages.append({"role": "user", "content": query})
|
|
142
172
|
return messages
|
|
@@ -22,5 +22,9 @@ class MediaTypeNotSupportedError(AskAIError):
|
|
|
22
22
|
"""Raised when a provider can't handle the requested media type."""
|
|
23
23
|
pass
|
|
24
24
|
|
|
25
|
+
class AskAIParsingError(AskAIError):
|
|
26
|
+
"""Raised when expect_json is True but the response cannot be parsed."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
25
29
|
# Alias for backwards compatibility internally
|
|
26
30
|
APIKeyError = AskAIConfigError
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
from typing import Dict, Any, Union
|
|
4
|
+
|
|
5
|
+
def clean_markdown(text: str) -> str:
|
|
6
|
+
"""Removes markdown code blocks like ```json ... ```"""
|
|
7
|
+
text = text.strip()
|
|
8
|
+
if text.startswith("```"):
|
|
9
|
+
# find the first newline
|
|
10
|
+
first_newline = text.find("\n")
|
|
11
|
+
if first_newline != -1:
|
|
12
|
+
text = text[first_newline+1:]
|
|
13
|
+
if text.endswith("```"):
|
|
14
|
+
text = text[:-3]
|
|
15
|
+
return text.strip()
|
|
16
|
+
|
|
17
|
+
def extract_code(text: str) -> str:
|
|
18
|
+
"""Extracts code blocks from text, ignoring conversational filler."""
|
|
19
|
+
text = str(text)
|
|
20
|
+
matches = re.findall(r'```(?:[\w]*\n)?(.*?)```', text, re.DOTALL)
|
|
21
|
+
if matches:
|
|
22
|
+
return "\n\n".join(m.strip() for m in matches)
|
|
23
|
+
return text
|
|
24
|
+
|
|
25
|
+
def strip_tags(text: str) -> str:
|
|
26
|
+
"""Removes XML/HTML tags, including <think>...</think> blocks."""
|
|
27
|
+
# First remove <think>...</think> entirely if we don't want the thought process
|
|
28
|
+
text = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL)
|
|
29
|
+
# Remove any other HTML tags
|
|
30
|
+
text = re.sub(r'<[^>]+>', '', text)
|
|
31
|
+
return text.strip()
|
|
32
|
+
|
|
33
|
+
def parse_json(text: str) -> Union[Dict[str, Any], list]:
|
|
34
|
+
"""Tries to parse JSON from text, employing cleaning if necessary."""
|
|
35
|
+
try:
|
|
36
|
+
return json.loads(text)
|
|
37
|
+
except json.JSONDecodeError:
|
|
38
|
+
cleaned = clean_markdown(text)
|
|
39
|
+
try:
|
|
40
|
+
return json.loads(cleaned)
|
|
41
|
+
except json.JSONDecodeError as e:
|
|
42
|
+
from .exceptions import AskAIParsingError
|
|
43
|
+
raise AskAIParsingError(f"Failed to parse JSON: {e}\nContent snippet: {text[:100]}...")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: askai-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, and more.
|
|
5
5
|
Author-email: Hossein Ghorbani <hosseingh1068@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -53,16 +53,6 @@ print(Groq().ask("Explain black holes like I'm 5").text)
|
|
|
53
53
|
- **Zero config**: Keys are pulled from the environment automatically
|
|
54
54
|
- **SDK-first, not a framework**: It stays out of your way.
|
|
55
55
|
|
|
56
|
-
## ⚖️ How it compares
|
|
57
|
-
|
|
58
|
-
| Feature | ask-ai | LangChain |
|
|
59
|
-
| -------------- | ------ | --------- |
|
|
60
|
-
| Setup time | 30 sec | 1 hour |
|
|
61
|
-
| Learning curve | ⭐ | ⭐⭐⭐⭐⭐ |
|
|
62
|
-
| Async support | ⏳ *(Coming soon)* | ⚠️ Complex |
|
|
63
|
-
| Retry/Timeout | ✅ Built-in | ❌ Manual |
|
|
64
|
-
| Gateway needed | ❌ No | ❌ No |
|
|
65
|
-
|
|
66
56
|
## 🚫 What this project is NOT
|
|
67
57
|
|
|
68
58
|
> ❌ Not an AI framework
|
|
@@ -73,6 +63,33 @@ It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
|
73
63
|
|
|
74
64
|
---
|
|
75
65
|
|
|
66
|
+
## 🛠️ Advanced Usage
|
|
67
|
+
|
|
68
|
+
### 🧰 Developer QoL Utilities (Auto-Parsing)
|
|
69
|
+
Stop writing Regex to clean up model outputs! `ask-ai` comes with built-in text processing flags:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from ask_ai import OpenAI
|
|
73
|
+
ai = OpenAI()
|
|
74
|
+
|
|
75
|
+
# 1. Clean Markdown (Removes ```json and ``` tags)
|
|
76
|
+
# Perfect for extracting raw data from models that wrap everything in markdown
|
|
77
|
+
clean_text = ai.ask("Write JSON", clean=True).text
|
|
78
|
+
|
|
79
|
+
# 2. Extract Code (Returns ONLY the code block, ignores conversational filler)
|
|
80
|
+
# Great for automation pipelines
|
|
81
|
+
code = ai.ask("Write a python ping script", code=True).text
|
|
82
|
+
|
|
83
|
+
# 3. Strip Tags (Removes <think> blocks and HTML)
|
|
84
|
+
# Essential for reasoning models like DeepSeek-R1
|
|
85
|
+
answer_only = ai.ask("What is 1+1?", strip=True).text
|
|
86
|
+
|
|
87
|
+
# 4. Enforce & Parse JSON (Directly returns a Parsed Python Dictionary)
|
|
88
|
+
# Adds JSON instructions to the prompt and safely runs json.loads()
|
|
89
|
+
data_dict = ai.ask("Extract user info", json=True).json
|
|
90
|
+
print(data_dict['name'])
|
|
91
|
+
```
|
|
92
|
+
|
|
76
93
|
## 🚀 Built-in Resiliency (Retries & Timeouts)
|
|
77
94
|
|
|
78
95
|
Build reliable apps without writing your own loops. `askai-python` handles rate limits (`429`) and network drops via an internal exponential backoff.
|
|
@@ -34,16 +34,6 @@ print(Groq().ask("Explain black holes like I'm 5").text)
|
|
|
34
34
|
- **Zero config**: Keys are pulled from the environment automatically
|
|
35
35
|
- **SDK-first, not a framework**: It stays out of your way.
|
|
36
36
|
|
|
37
|
-
## ⚖️ How it compares
|
|
38
|
-
|
|
39
|
-
| Feature | ask-ai | LangChain |
|
|
40
|
-
| -------------- | ------ | --------- |
|
|
41
|
-
| Setup time | 30 sec | 1 hour |
|
|
42
|
-
| Learning curve | ⭐ | ⭐⭐⭐⭐⭐ |
|
|
43
|
-
| Async support | ⏳ *(Coming soon)* | ⚠️ Complex |
|
|
44
|
-
| Retry/Timeout | ✅ Built-in | ❌ Manual |
|
|
45
|
-
| Gateway needed | ❌ No | ❌ No |
|
|
46
|
-
|
|
47
37
|
## 🚫 What this project is NOT
|
|
48
38
|
|
|
49
39
|
> ❌ Not an AI framework
|
|
@@ -54,6 +44,33 @@ It does one thing perfectly: **Simplifying the API call to LLMs.**
|
|
|
54
44
|
|
|
55
45
|
---
|
|
56
46
|
|
|
47
|
+
## 🛠️ Advanced Usage
|
|
48
|
+
|
|
49
|
+
### 🧰 Developer QoL Utilities (Auto-Parsing)
|
|
50
|
+
Stop writing Regex to clean up model outputs! `ask-ai` comes with built-in text processing flags:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from ask_ai import OpenAI
|
|
54
|
+
ai = OpenAI()
|
|
55
|
+
|
|
56
|
+
# 1. Clean Markdown (Removes ```json and ``` tags)
|
|
57
|
+
# Perfect for extracting raw data from models that wrap everything in markdown
|
|
58
|
+
clean_text = ai.ask("Write JSON", clean=True).text
|
|
59
|
+
|
|
60
|
+
# 2. Extract Code (Returns ONLY the code block, ignores conversational filler)
|
|
61
|
+
# Great for automation pipelines
|
|
62
|
+
code = ai.ask("Write a python ping script", code=True).text
|
|
63
|
+
|
|
64
|
+
# 3. Strip Tags (Removes <think> blocks and HTML)
|
|
65
|
+
# Essential for reasoning models like DeepSeek-R1
|
|
66
|
+
answer_only = ai.ask("What is 1+1?", strip=True).text
|
|
67
|
+
|
|
68
|
+
# 4. Enforce & Parse JSON (Directly returns a Parsed Python Dictionary)
|
|
69
|
+
# Adds JSON instructions to the prompt and safely runs json.loads()
|
|
70
|
+
data_dict = ai.ask("Extract user info", json=True).json
|
|
71
|
+
print(data_dict['name'])
|
|
72
|
+
```
|
|
73
|
+
|
|
57
74
|
## 🚀 Built-in Resiliency (Retries & Timeouts)
|
|
58
75
|
|
|
59
76
|
Build reliable apps without writing your own loops. `askai-python` handles rate limits (`429`) and network drops via an internal exponential backoff.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "askai-python"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "AI Made Stupid Simple. Unified API for OpenAI, Groq, Google, and more."
|
|
9
9
|
readme = "docs/PYPI_README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|