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.
Files changed (27) hide show
  1. {askai_python-0.2.0/askai_python.egg-info → askai_python-0.2.1}/PKG-INFO +28 -11
  2. {askai_python-0.2.0 → askai_python-0.2.1}/README.md +26 -12
  3. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/base.py +37 -7
  4. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/exceptions.py +4 -0
  5. askai_python-0.2.1/ask_ai/utils.py +43 -0
  6. {askai_python-0.2.0 → askai_python-0.2.1/askai_python.egg-info}/PKG-INFO +28 -11
  7. {askai_python-0.2.0 → askai_python-0.2.1}/askai_python.egg-info/SOURCES.txt +1 -0
  8. {askai_python-0.2.0 → askai_python-0.2.1}/docs/PYPI_README.md +27 -10
  9. {askai_python-0.2.0 → askai_python-0.2.1}/pyproject.toml +1 -1
  10. {askai_python-0.2.0 → askai_python-0.2.1}/LICENSE +0 -0
  11. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/__init__.py +0 -0
  12. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/__main__.py +0 -0
  13. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/cli.py +0 -0
  14. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/config.py +0 -0
  15. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/media.py +0 -0
  16. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/__init__.py +0 -0
  17. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/anthropic_provider.py +0 -0
  18. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/azure_provider.py +0 -0
  19. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/google_provider.py +0 -0
  20. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/groq_provider.py +0 -0
  21. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/openai_provider.py +0 -0
  22. {askai_python-0.2.0 → askai_python-0.2.1}/ask_ai/providers/openrouter_provider.py +0 -0
  23. {askai_python-0.2.0 → askai_python-0.2.1}/askai_python.egg-info/dependency_links.txt +0 -0
  24. {askai_python-0.2.0 → askai_python-0.2.1}/askai_python.egg-info/requires.txt +0 -0
  25. {askai_python-0.2.0 → askai_python-0.2.1}/askai_python.egg-info/top_level.txt +0 -0
  26. {askai_python-0.2.0 → askai_python-0.2.1}/setup.cfg +0 -0
  27. {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.0
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
- ### Built-in Retries & Timeout (Resiliency)
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.text = text
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
- return self._send_request(messages, final_config, output_type)
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
- if config.system_message:
137
- messages.append({"role": "system", "content": config.system_message})
138
- elif self.persona:
139
- messages.append({"role": "system", "content": self.persona})
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.0
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.
@@ -8,6 +8,7 @@ ask_ai/cli.py
8
8
  ask_ai/config.py
9
9
  ask_ai/exceptions.py
10
10
  ask_ai/media.py
11
+ ask_ai/utils.py
11
12
  ask_ai/providers/__init__.py
12
13
  ask_ai/providers/anthropic_provider.py
13
14
  ask_ai/providers/azure_provider.py
@@ -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.0"
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