autonomous-app 0.3.37__py3-none-any.whl → 0.3.39__py3-none-any.whl

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.
autonomous/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.3.37"
1
+ __version__ = "0.3.39"
2
2
 
3
3
  from dotenv import load_dotenv
4
4
 
@@ -2,13 +2,8 @@ import io
2
2
  import json
3
3
  import os
4
4
  import random
5
- import re
6
5
 
7
- import numpy as np
8
- import pymongo
9
- import redis
10
6
  import requests
11
- from bson.objectid import ObjectId
12
7
  from pydub import AudioSegment
13
8
 
14
9
  from autonomous import log
@@ -44,17 +39,39 @@ class LocalAIModel(AutoModel):
44
39
  return json.dumps(schema, indent=2)
45
40
 
46
41
  def _clean_json_response(self, text):
47
- """Helper to strip markdown artifacts from JSON responses."""
42
+ """
43
+ Robust cleaner for Llama 3 outputs.
44
+ It handles markdown blocks, chatter before/after, and malformed endings.
45
+ """
48
46
  text = text.strip()
49
- # Remove ```json ... ``` or just ``` ... ``` wrapper
50
- if text.startswith("```"):
51
- # Find the first newline to skip the language tag (e.g., "json")
52
- first_newline = text.find("\n")
53
- if first_newline != -1:
54
- text = text[first_newline + 1 :]
55
- # Remove the closing backticks
56
- if text.endswith("```"):
57
- text = text[:-3]
47
+
48
+ # 1. Strip Markdown Code Blocks (```json ... ```)
49
+ if "```" in text:
50
+ import re
51
+
52
+ # Regex to capture content inside ```json ... ``` or just ``` ... ```
53
+ # flags=re.DOTALL allows . to match newlines
54
+ pattern = r"```(?:json)?\s*(\{.*?\})\s*```"
55
+ match = re.search(pattern, text, re.DOTALL)
56
+ if match:
57
+ text = match.group(1)
58
+ else:
59
+ # Fallback: simple finding if regex fails due to weird chars
60
+ start = text.find("```")
61
+ end = text.rfind("```")
62
+ # Adjust start to skip the "json" part if present
63
+ first_newline = text.find("\n", start)
64
+ if first_newline != -1 and first_newline < end:
65
+ text = text[first_newline:end]
66
+
67
+ # 2. Heuristic extraction: Find the first '{' and the last '}'
68
+ # This fixes cases where Llama says "Here is the JSON: { ... }"
69
+ start_idx = text.find("{")
70
+ end_idx = text.rfind("}")
71
+
72
+ if start_idx != -1 and end_idx != -1:
73
+ text = text[start_idx : end_idx + 1]
74
+
58
75
  return text.strip()
59
76
 
60
77
  def generate_json(
@@ -62,51 +79,62 @@ class LocalAIModel(AutoModel):
62
79
  ):
63
80
  schema_str = self._convert_tools_to_json_schema(function)
64
81
 
65
- # 1. Base System Prompt with Context Anchoring
82
+ # 1. Improved System Prompt
83
+ # We explicitly warn about nested quotes, which is the #1 killer of complex JSON
66
84
  full_system_prompt = (
67
85
  f"{self.instructions}. {additional_instructions}\n"
68
86
  f"You are a strict JSON generator. Output ONLY a valid JSON object matching this schema:\n"
69
87
  f"{schema_str}\n"
70
- f"IMPORTANT: Do not include markdown formatting (like ```json), introductions, or explanations.\n"
88
+ f"IMPORTANT RULES:\n"
89
+ f"1. Do not include markdown formatting or explanations.\n"
90
+ f"2. DOUBLE CHECK nested quotes inside strings. Escape them properly.\n"
91
+ f"3. Ensure all arrays and objects are closed.\n"
71
92
  )
72
93
 
73
94
  if context:
74
95
  full_system_prompt += (
75
96
  f"\n\n### GROUND TRUTH CONTEXT ###\n"
76
- f"You must strictly adhere to the following context. "
77
- f"If this context contradicts your internal knowledge (e.g., physics, facts), "
78
- f"YOU MUST FOLLOW THE CONTEXT.\n"
97
+ f"Adhere strictly to this context:\n"
79
98
  f"{json.dumps(context, indent=2)}"
80
99
  )
81
100
  elif uri:
82
101
  full_system_prompt += f"Use the following URI for reference: {uri}"
83
102
 
84
- # 3. Send to Ollama with JSON Mode
103
+ # 3. Payload with INCREASED CONTEXT and LOWER TEMPERATURE
85
104
  payload = {
86
105
  "model": self._json_model,
87
106
  "messages": [
88
107
  {"role": "system", "content": full_system_prompt},
89
108
  {"role": "user", "content": message},
90
109
  ],
91
- "format": "json", # <--- CRITICAL: Forces valid JSON output
110
+ "format": "json",
92
111
  "stream": False,
93
112
  "keep_alive": "24h",
113
+ "options": {
114
+ "num_ctx": 8192, # <--- Prevents cutoff on large schemas
115
+ "temperature": 0.8, # <--- INCREASED from 0.2 to 0.8 for creativity
116
+ "top_p": 0.9, # <--- Adds nuance to word choice
117
+ "repeat_penalty": 1.1, # <--- Prevents it from saying "rust" 5 times
118
+ },
94
119
  }
95
120
 
121
+ log("==== LocalAI JSON Payload ====", payload, _print=True)
122
+ result_text = ""
96
123
  try:
97
124
  # print(f"==== {self._ollama_url}: LocalAI JSON Payload ====")
98
125
  response = requests.post(f"{self._ollama_url}/chat", json=payload)
126
+ log(response)
99
127
  response.raise_for_status()
100
128
 
101
129
  result_text = response.json().get("message", {}).get("content", "{}")
102
130
 
103
- # Clean up potential markdown artifacts
131
+ # Clean
104
132
  clean_text = self._clean_json_response(result_text)
105
133
 
106
134
  # Parse
107
135
  result_dict = json.loads(clean_text)
108
136
 
109
- # Unwrap if the model nested it inside "parameters" (common Llama quirk)
137
+ # Unwrap
110
138
  if "parameters" in result_dict and isinstance(
111
139
  result_dict["parameters"], dict
112
140
  ):
@@ -116,8 +144,16 @@ class LocalAIModel(AutoModel):
116
144
  return result_dict
117
145
 
118
146
  except Exception as e:
147
+ # If it fails, print the RAW text so you can see WHERE it broke.
119
148
  log(f"==== LocalAI JSON Error: {e} ====", _print=True)
120
- raise e
149
+ if result_text:
150
+ log(
151
+ f"--- FAILED RAW OUTPUT ---\n{result_text}\n-----------------------",
152
+ _print=True,
153
+ )
154
+
155
+ # Returning empty prevents the whole app from dying on one bad generation.
156
+ return {}
121
157
 
122
158
  def generate_text(self, message, additional_instructions="", uri="", context={}):
123
159
  # 1. Base System Prompt
@@ -208,6 +244,27 @@ class LocalAIModel(AutoModel):
208
244
  log(f"TTS Error: {e}", _print=True)
209
245
  return None
210
246
 
247
+ # ... inside LocalAIModel class ...
248
+
249
+ def _get_dimensions(self, aspect_ratio):
250
+ """
251
+ Maps abstract aspect ratios to optimal SDXL resolutions.
252
+ SDXL performs best at ~1024x1024 total pixels.
253
+ """
254
+ resolutions = {
255
+ "1:1": (1024, 1024),
256
+ "3:4": (896, 1152),
257
+ "4:3": (1152, 896),
258
+ "16:9": (1216, 832),
259
+ "2K": (2048, 1080),
260
+ "4K": (3840, 2160),
261
+ "9:16": (832, 1216),
262
+ "3:2": (1216, 832),
263
+ "2:3": (832, 1216),
264
+ }
265
+ # Default to 1:1 (1024x1024) if unknown
266
+ return resolutions.get(aspect_ratio, (1024, 1024))
267
+
211
268
  def generate_image(
212
269
  self,
213
270
  prompt,
@@ -216,32 +273,55 @@ class LocalAIModel(AutoModel):
216
273
  aspect_ratio="3:4",
217
274
  image_size="2K",
218
275
  ):
276
+ # 1. CLIP Token Limit Fix (Auto-Summarize)
277
+ if len(prompt) > 300:
278
+ log("⚠️ Prompt exceeds CLIP limit. rewriting...", _print=True)
279
+ summary_instruction = (
280
+ "Convert the description into a comma-separated Stable Diffusion prompt. "
281
+ "Keep visual elements and style. Under 50 words."
282
+ )
283
+ new_prompt = self.generate_text(
284
+ message=prompt, additional_instructions=summary_instruction, context={}
285
+ )
286
+ if new_prompt and len(new_prompt) > 10:
287
+ prompt = new_prompt
288
+
289
+ # 2. Resolution Calculation
290
+ width, height = self._get_dimensions(aspect_ratio)
291
+
292
+ # 3. Construct Payload
293
+ # We send both the abstract params (for logging/metadata)
294
+ # and the concrete pixels (for the engine).
295
+ data = {
296
+ "prompt": prompt,
297
+ "negative_prompt": negative_prompt,
298
+ "aspect_ratio": aspect_ratio,
299
+ "width": width, # <--- Calculated Pixel Width
300
+ "height": height, # <--- Calculated Pixel Height
301
+ }
302
+
219
303
  try:
220
- data = {"prompt": prompt, "negative_prompt": negative_prompt}
304
+ # Handle Files (Dict -> List of Tuples for requests)
221
305
  img_files = {}
222
- if files:
306
+ if files and isinstance(files, dict):
223
307
  for fn, f_bytes in files.items():
224
308
  if isinstance(f_bytes, bytes):
225
309
  file_obj = io.BytesIO(f_bytes)
226
310
  else:
227
311
  file_obj = f_bytes
228
312
  img_files["file"] = (fn, file_obj, "image/png")
313
+
314
+ # Send Request
315
+ if img_files:
229
316
  response = requests.post(
230
317
  f"{self._media_url}/generate-image", data=data, files=img_files
231
318
  )
232
319
  else:
233
320
  response = requests.post(f"{self._media_url}/generate-image", data=data)
321
+
234
322
  response.raise_for_status()
235
323
  return response.content
324
+
236
325
  except Exception as e:
237
326
  log(f"Image Gen Error: {e}", _print=True)
238
327
  return None
239
-
240
- def list_voices(self, filters=[]):
241
- if not filters:
242
- return list(self.VOICES.keys())
243
- voices = []
244
- for voice, attribs in self.VOICES.items():
245
- if any(f.lower() in attribs for f in filters):
246
- voices.append(voice)
247
- return voices
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: autonomous-app
3
- Version: 0.3.37
3
+ Version: 0.3.39
4
4
  Summary: Containerized application framework built on Flask with additional libraries and tools for rapid development of web applications.
5
5
  Author-email: Steven A Moore <samoore@binghamton.edu>
6
6
  Project-URL: homepage, https://github.com/Sallenmoore/autonomous
@@ -1,4 +1,4 @@
1
- autonomous/__init__.py,sha256=Y2FQQrtDQSpcFBuNs_0bheHgw0K4d8HBaU-hbKw1cu4,95
1
+ autonomous/__init__.py,sha256=q_VjpmLkH8iiS1RkA2Lz2bc4RrMxOIWaOGNgqo7YkTE,95
2
2
  autonomous/cli.py,sha256=z4AaGeWNW_uBLFAHng0J_lfS9v3fXemK1PeT85u4Eo4,42
3
3
  autonomous/logger.py,sha256=NQtgEaTWNAWfLSgqSP7ksXj1GpOuCgoUV711kSMm-WA,2022
4
4
  autonomous/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -9,7 +9,7 @@ autonomous/ai/jsonagent.py,sha256=OZeQthp5WOSCV6pmbPfPQRjARkvbK5lk7A0QTEPUrUk,12
9
9
  autonomous/ai/textagent.py,sha256=0y2Hvb9pup1OnsA51hGPcD8yllZOZtztDLQvCNYABaw,1043
10
10
  autonomous/ai/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  autonomous/ai/models/gemini.py,sha256=eu48gywNFpUFaqBt-4MFX2oRM5IED9rUTgtavM_HRG0,14468
12
- autonomous/ai/models/local_model.py,sha256=m_rEs1BtBhzbmNTdYnyxxOiRNkoBaapk3dwl1QPtB-I,9038
12
+ autonomous/ai/models/local_model.py,sha256=JbU_BC-7qqgF8hsuk-7S3l2CsxUFc-wsL1a8ID2AdNY,12068
13
13
  autonomous/apis/version_control/GHCallbacks.py,sha256=AyiUlYfV5JePi11GVyqYyXoj5UTbPKzS-HRRI94rjJo,1069
14
14
  autonomous/apis/version_control/GHOrganization.py,sha256=mi2livdsGurKiifbvuLwiFbdDzL77IlEfhwEa-tG77I,1155
15
15
  autonomous/apis/version_control/GHRepo.py,sha256=hTFHMkxSbSlVELfh8S6mq6ijkIKPRQO-Q5775ZjRKD4,4622
@@ -55,7 +55,7 @@ autonomous/taskrunner/__init__.py,sha256=ughX-QfWBas5W3aB2SiF887SWJ3Dzc2X43Yxtmp
55
55
  autonomous/taskrunner/autotasks.py,sha256=2zRaqHYqfdlgC_BQm6B6D2svN1ukyWeJJHwweZFHVoo,2616
56
56
  autonomous/taskrunner/task_router.py,sha256=W09HtRUuhwlnGxM5w4l6Hzw6mfS6L4ljWiMzD3ZVFeU,601
57
57
  autonomous/utils/markdown.py,sha256=tf8vlHARiQO1X_aGbqlYozzP_TbdiDRT9EEP6aFRQo0,2153
58
- autonomous_app-0.3.37.dist-info/METADATA,sha256=Y3_569_vKq8_ZOmsy2zHYbq0ynllStrQKQaZ-ZFDufg,3024
59
- autonomous_app-0.3.37.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
60
- autonomous_app-0.3.37.dist-info/top_level.txt,sha256=ZyxWWDdbvZekF3UFunxl4BQsVDb_FOW3eTn0vun_jb4,11
61
- autonomous_app-0.3.37.dist-info/RECORD,,
58
+ autonomous_app-0.3.39.dist-info/METADATA,sha256=9rRFFf_IvOxHp4PHGlYyac6GbeVVYViT5614cANQvwE,3024
59
+ autonomous_app-0.3.39.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
60
+ autonomous_app-0.3.39.dist-info/top_level.txt,sha256=ZyxWWDdbvZekF3UFunxl4BQsVDb_FOW3eTn0vun_jb4,11
61
+ autonomous_app-0.3.39.dist-info/RECORD,,