autonomous-app 0.2.25__tar.gz → 0.3.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 (79) hide show
  1. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/PKG-INFO +7 -8
  2. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/pyproject.toml +3 -3
  3. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/requirements.txt +4 -9
  4. autonomous_app-0.3.1/src/autonomous/__init__.py +7 -0
  5. autonomous_app-0.3.1/src/autonomous/ai/audioagent.py +32 -0
  6. autonomous_app-0.3.1/src/autonomous/ai/imageagent.py +31 -0
  7. autonomous_app-0.3.1/src/autonomous/ai/jsonagent.py +40 -0
  8. autonomous_app-0.3.1/src/autonomous/ai/models/openai.py +308 -0
  9. autonomous_app-0.3.1/src/autonomous/ai/oaiagent.py +40 -0
  10. autonomous_app-0.3.1/src/autonomous/ai/textagent.py +35 -0
  11. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/autoauth.py +11 -11
  12. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/user.py +24 -11
  13. autonomous_app-0.3.1/src/autonomous/db/__init__.py +42 -0
  14. autonomous_app-0.3.1/src/autonomous/db/base/__init__.py +33 -0
  15. autonomous_app-0.3.1/src/autonomous/db/base/common.py +62 -0
  16. autonomous_app-0.3.1/src/autonomous/db/base/datastructures.py +476 -0
  17. autonomous_app-0.3.1/src/autonomous/db/base/document.py +1230 -0
  18. autonomous_app-0.3.1/src/autonomous/db/base/fields.py +767 -0
  19. autonomous_app-0.3.1/src/autonomous/db/base/metaclasses.py +468 -0
  20. autonomous_app-0.3.1/src/autonomous/db/base/utils.py +22 -0
  21. autonomous_app-0.3.1/src/autonomous/db/common.py +79 -0
  22. autonomous_app-0.3.1/src/autonomous/db/connection.py +472 -0
  23. autonomous_app-0.3.1/src/autonomous/db/context_managers.py +313 -0
  24. autonomous_app-0.3.1/src/autonomous/db/dereference.py +291 -0
  25. autonomous_app-0.3.1/src/autonomous/db/document.py +1141 -0
  26. autonomous_app-0.3.1/src/autonomous/db/errors.py +165 -0
  27. autonomous_app-0.3.1/src/autonomous/db/fields.py +2732 -0
  28. autonomous_app-0.3.1/src/autonomous/db/mongodb_support.py +24 -0
  29. autonomous_app-0.3.1/src/autonomous/db/pymongo_support.py +80 -0
  30. autonomous_app-0.3.1/src/autonomous/db/queryset/__init__.py +28 -0
  31. autonomous_app-0.3.1/src/autonomous/db/queryset/base.py +2033 -0
  32. autonomous_app-0.3.1/src/autonomous/db/queryset/field_list.py +88 -0
  33. autonomous_app-0.3.1/src/autonomous/db/queryset/manager.py +58 -0
  34. autonomous_app-0.3.1/src/autonomous/db/queryset/queryset.py +189 -0
  35. autonomous_app-0.3.1/src/autonomous/db/queryset/transform.py +527 -0
  36. autonomous_app-0.3.1/src/autonomous/db/queryset/visitor.py +189 -0
  37. autonomous_app-0.3.1/src/autonomous/db/signals.py +59 -0
  38. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/logger.py +3 -0
  39. autonomous_app-0.3.1/src/autonomous/model/autoattr.py +120 -0
  40. autonomous_app-0.3.1/src/autonomous/model/automodel.py +213 -0
  41. autonomous_app-0.3.1/src/autonomous/storage/__init__.py +0 -0
  42. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/storage/imagestorage.py +9 -54
  43. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/tasks/autotask.py +0 -25
  44. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous_app.egg-info/PKG-INFO +7 -8
  45. autonomous_app-0.3.1/src/autonomous_app.egg-info/SOURCES.txt +65 -0
  46. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous_app.egg-info/requires.txt +4 -5
  47. autonomous_app-0.2.25/src/autonomous/__init__.py +0 -4
  48. autonomous_app-0.2.25/src/autonomous/ai/oaiagent.py +0 -214
  49. autonomous_app-0.2.25/src/autonomous/db/autodb.py +0 -86
  50. autonomous_app-0.2.25/src/autonomous/db/table.py +0 -156
  51. autonomous_app-0.2.25/src/autonomous/errors/__init__.py +0 -1
  52. autonomous_app-0.2.25/src/autonomous/errors/danglingreferenceerror.py +0 -8
  53. autonomous_app-0.2.25/src/autonomous/model/__init__.py +0 -1
  54. autonomous_app-0.2.25/src/autonomous/model/autoattribute.py +0 -20
  55. autonomous_app-0.2.25/src/autonomous/model/automodel.py +0 -400
  56. autonomous_app-0.2.25/src/autonomous/model/orm.py +0 -86
  57. autonomous_app-0.2.25/src/autonomous/model/serializer.py +0 -110
  58. autonomous_app-0.2.25/src/autonomous_app.egg-info/SOURCES.txt +0 -41
  59. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/LICENSE +0 -0
  60. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/README.md +0 -0
  61. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/setup.cfg +0 -0
  62. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/setup.py +0 -0
  63. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/ai/__init__.py +0 -0
  64. {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/ai/models}/__init__.py +0 -0
  65. {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/GHCallbacks.py +0 -0
  66. {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/GHOrganization.py +0 -0
  67. {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/GHRepo.py +0 -0
  68. {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/GHVersionControl.py +0 -0
  69. {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/__init__.py +0 -0
  70. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/__init__.py +0 -0
  71. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/github.py +0 -0
  72. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/google.py +0 -0
  73. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/cli.py +0 -0
  74. {autonomous_app-0.2.25/src/autonomous/db → autonomous_app-0.3.1/src/autonomous/model}/__init__.py +0 -0
  75. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/storage/localstorage.py +0 -0
  76. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/tasks/__init__.py +0 -0
  77. {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/utils}/markdown.py +0 -0
  78. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous_app.egg-info/dependency_links.txt +0 -0
  79. {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous_app.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: autonomous-app
3
- Version: 0.2.25
3
+ Version: 0.3.1
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
  License: MIT License
@@ -25,27 +25,26 @@ License: MIT License
25
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
26
  SOFTWARE.
27
27
  Project-URL: homepage, https://github.com/Sallenmoore/autonomous
28
- Classifier: Programming Language :: Python :: 3.11
28
+ Classifier: Programming Language :: Python :: 3.12
29
29
  Classifier: License :: OSI Approved :: MIT License
30
30
  Classifier: Operating System :: OS Independent
31
- Requires-Python: >=3.11
31
+ Requires-Python: >=3.12
32
32
  Description-Content-Type: text/markdown
33
33
  License-File: LICENSE
34
34
  Requires-Dist: Flask
35
- Requires-Dist: icecream
36
35
  Requires-Dist: setuptools
37
36
  Requires-Dist: python-dotenv
37
+ Requires-Dist: blinker
38
38
  Requires-Dist: PyGithub
39
39
  Requires-Dist: pygit2
40
- Requires-Dist: cloudinary
41
40
  Requires-Dist: pillow
42
- Requires-Dist: pymongo
41
+ Requires-Dist: redis
43
42
  Requires-Dist: jsmin
44
43
  Requires-Dist: requests
45
44
  Requires-Dist: gunicorn
46
45
  Requires-Dist: Authlib
47
- Requires-Dist: openai>=1.21.1
48
- Requires-Dist: validators
46
+ Requires-Dist: rq
47
+ Requires-Dist: openai>=1.42
49
48
  Requires-Dist: dateparser
50
49
  Requires-Dist: python-slugify
51
50
 
@@ -10,10 +10,10 @@ authors = [
10
10
  description = "Containerized application framework built on Flask with additional libraries and tools for rapid development of web applications."
11
11
  dynamic = ["version", "dependencies"]
12
12
  readme = "README.md"
13
- requires-python = ">=3.11"
13
+ requires-python = ">=3.12"
14
14
  license={file = "LICENSE"}
15
15
  classifiers=[
16
- "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
17
  "License :: OSI Approved :: MIT License",
18
18
  "Operating System :: OS Independent",
19
19
  ]
@@ -35,7 +35,7 @@ where = ["src"]
35
35
  include = ["autonomous*"]
36
36
 
37
37
  [tool.pytest.ini_options]
38
- addopts = "--cov=autonomous -rx -l -x -s --log-level=INFO --ignore=src --no-cov-on-fail -v --pdb --cov-reset"
38
+ addopts = "--cov=autonomous -rx -l -x -s --log-level=INFO --ignore=src --no-cov-on-fail -v --pdb --cov-reset "
39
39
 
40
40
  testpaths = [
41
41
  "tests",
@@ -8,21 +8,18 @@ Flask
8
8
 
9
9
  ##### Debugging, Logging, and Documentation #####
10
10
 
11
- icecream
12
-
13
11
  ##### System #####
14
12
 
15
13
  setuptools
16
14
  python-dotenv
15
+ blinker
17
16
 
18
17
  ##### Storage #####
19
18
 
20
19
  PyGithub
21
20
  pygit2
22
-
23
- cloudinary
24
21
  pillow
25
- pymongo
22
+ redis
26
23
 
27
24
  ##### Frontend #####
28
25
 
@@ -38,14 +35,12 @@ gunicorn
38
35
  Authlib
39
36
 
40
37
  ##### Async #####
41
-
38
+ rq
42
39
 
43
40
  ##### AI #####
44
41
 
45
- openai >= 1.21.1
42
+ openai >= 1.42
46
43
 
47
44
  ##### Parsing #####
48
-
49
- validators
50
45
  dateparser
51
46
  python-slugify
@@ -0,0 +1,7 @@
1
+ __version__ = "0.3.01"
2
+
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ from .logger import log
@@ -0,0 +1,32 @@
1
+ from autonomous import log
2
+ from autonomous.model.autoattr import ReferenceAttr, StringAttr
3
+ from autonomous.model.automodel import AutoModel
4
+
5
+ from .models.openai import OpenAIModel
6
+
7
+
8
+ class AudioAgent(AutoModel):
9
+ client = ReferenceAttr(choices=[OpenAIModel])
10
+ name = StringAttr(default="audioagent")
11
+ instructions = StringAttr(
12
+ default="You are highly skilled AI trained to assist with generating audio files."
13
+ )
14
+ description = StringAttr(
15
+ default="A helpful AI assistant trained to assist with generating audio files."
16
+ )
17
+
18
+ _ai_model = OpenAIModel
19
+
20
+ def get_client(self):
21
+ if self.client is None:
22
+ self.client = self._ai_model(
23
+ name=self.name,
24
+ instructions=self.instructions,
25
+ description=self.description,
26
+ )
27
+ self.client.save()
28
+ self.save()
29
+ return self.client
30
+
31
+ def generate(self, prompt, file_path, **kwargs):
32
+ return self.get_client().generate_audio(prompt, file_path, **kwargs)
@@ -0,0 +1,31 @@
1
+ from autonomous.model.autoattr import ReferenceAttr, StringAttr
2
+ from autonomous.model.automodel import AutoModel
3
+
4
+ from .models.openai import OpenAIModel
5
+
6
+
7
+ class ImageAgent(AutoModel):
8
+ client = ReferenceAttr(choices=[OpenAIModel])
9
+ name = StringAttr(default="imageagent")
10
+ instructions = StringAttr(
11
+ default="You are highly skilled AI trained to assist with generating images."
12
+ )
13
+ description = StringAttr(
14
+ default="A helpful AI assistant trained to assist with generating images."
15
+ )
16
+
17
+ _ai_model = OpenAIModel
18
+
19
+ def get_client(self):
20
+ if self.client is None:
21
+ self.client = self._ai_model(
22
+ name=self.name,
23
+ instructions=self.instructions,
24
+ description=self.description,
25
+ )
26
+ self.client.save()
27
+ self.save()
28
+ return self.client
29
+
30
+ def generate(self, prompt, **kwargs):
31
+ return self.get_client().generate_image(prompt, **kwargs)
@@ -0,0 +1,40 @@
1
+ import json
2
+
3
+ from autonomous.model.autoattr import ReferenceAttr, StringAttr
4
+ from autonomous.model.automodel import AutoModel
5
+
6
+ from .models.openai import OpenAIModel
7
+
8
+
9
+ class JSONAgent(AutoModel):
10
+ client = ReferenceAttr(choices=[OpenAIModel])
11
+ name = StringAttr(default="jsonagent")
12
+ instructions = StringAttr(
13
+ default="You are highly skilled AI trained to assist with generating JSON formatted data."
14
+ )
15
+ description = StringAttr(
16
+ default="A helpful AI assistant trained to assist with generating JSON formatted data."
17
+ )
18
+
19
+ _ai_model = OpenAIModel
20
+
21
+ def get_client(self):
22
+ if self.client is None:
23
+ self.client = self._ai_model(
24
+ name=self.name,
25
+ instructions=self.instructions,
26
+ description=self.description,
27
+ )
28
+ self.client.save()
29
+ self.save()
30
+ return self.client
31
+
32
+ def generate(self, messages, function, additional_instructions=""):
33
+ result = self.get_client().generate_json(
34
+ messages, function, additional_instructions
35
+ )
36
+ if isinstance(result, str):
37
+ result = json.loads(result)
38
+ elif not isinstance(result, dict):
39
+ raise ValueError(f"Invalid JSON response from AI model.\n\n{result}")
40
+ return result
@@ -0,0 +1,308 @@
1
+ import json
2
+ import os
3
+ import random
4
+ import time
5
+ from base64 import b64decode
6
+
7
+ import openai
8
+ from openai import NotFoundError as openai_NotFoundError
9
+ from openai import OpenAI
10
+
11
+ from autonomous import log
12
+ from autonomous.model.autoattr import DictAttr, ListAttr, StringAttr
13
+ from autonomous.model.automodel import AutoModel
14
+
15
+
16
+ class OpenAIModel(AutoModel):
17
+ _client = None
18
+ _text_model = "gpt-4o-mini"
19
+ _image_model = "dall-e-3"
20
+ _json_model = "gpt-4o"
21
+ agent_id = StringAttr()
22
+ messages = ListAttr(StringAttr(default=[]))
23
+ tools = DictAttr()
24
+ vector_store = StringAttr()
25
+ name = StringAttr(default="agent")
26
+ instructions = StringAttr(
27
+ default="You are highly skilled AI trained to assist with various tasks."
28
+ )
29
+ description = StringAttr(
30
+ default="A helpful AI assistant trained to assist with various tasks."
31
+ )
32
+
33
+ @property
34
+ def client(self):
35
+ if not self._client:
36
+ self._client = OpenAI(api_key=os.environ.get("OPENAI_KEY"))
37
+ return self._client
38
+
39
+ def clear_agent(self):
40
+ if self.agent_id:
41
+ self.client.beta.assistants.delete(self.agent_id)
42
+ self.agent_id = ""
43
+ self.save()
44
+
45
+ def clear_agents(self):
46
+ assistants = self.client.beta.assistants.list().data
47
+ log(assistants)
48
+ for assistant in assistants:
49
+ log(f"==== Deleting Agent with ID: {assistant.id} ====")
50
+ try:
51
+ self.client.beta.assistants.delete(assistant.id)
52
+ except openai_NotFoundError:
53
+ log(f"==== Agent with ID: {assistant.id} not found ====")
54
+ self.agent_id = ""
55
+ self.save()
56
+
57
+ def _get_agent_id(self):
58
+ if not self.agent_id or not self.client.beta.assistants.retrieve(self.agent_id):
59
+ agent = self.client.beta.assistants.create(
60
+ instructions=self.instructions,
61
+ description=self.description,
62
+ name=self.name,
63
+ model=self._json_model,
64
+ )
65
+ self.agent_id = agent.id
66
+ log(f"==== Creating Agent with ID: {self.agent_id} ====")
67
+ self.save()
68
+ return self.agent_id
69
+
70
+ def clear_files(self, file_id=None, all=False):
71
+ if not file_id:
72
+ store_files = self.client.files.list().data
73
+
74
+ for vs in self.client.beta.vector_stores.list().data:
75
+ try:
76
+ self.client.beta.vector_stores.delete(vs.id)
77
+ except openai_NotFoundError:
78
+ log(f"==== Vector Store {vs.id} not found ====")
79
+ if all:
80
+ for sf in store_files:
81
+ self.client.files.delete(file_id=sf.id)
82
+ else:
83
+ self.client.files.delete(file_id=file_id)
84
+ self.tools.pop("file_search", None)
85
+ self.save()
86
+ return self.client.files.list()
87
+
88
+ def attach_file(self, file_contents, filename="dbdata.json"):
89
+ # Upload the user provided file to OpenAI
90
+ self.tools["file_search"] = {"type": "file_search"}
91
+ # Create a vector store
92
+ if vs := self.client.beta.vector_stores.list().data:
93
+ self.vector_store = vs[0].id
94
+ else:
95
+ self.vector_store = self.client.beta.vector_stores.create(
96
+ name="Data Reference",
97
+ expires_after={"anchor": "last_active_at", "days": 14},
98
+ ).id
99
+
100
+ file_obj = self.client.files.create(
101
+ file=(filename, file_contents), purpose="assistants"
102
+ )
103
+
104
+ self.client.beta.vector_stores.files.create(
105
+ vector_store_id=self.vector_store,
106
+ file_id=file_obj.id,
107
+ )
108
+ self.client.beta.assistants.update(
109
+ self._get_agent_id(),
110
+ tools=list(self.tools.values()),
111
+ tool_resources={"file_search": {"vector_store_ids": [self.vector_store]}},
112
+ )
113
+ self.save()
114
+ return file_obj.id
115
+
116
+ def _add_function(self, user_function):
117
+ user_function["strict"] = True
118
+ user_function["parameters"]["additionalProperties"] = False
119
+ if not user_function["parameters"].get("required"):
120
+ user_function["parameters"]["required"] = list(
121
+ user_function["parameters"]["properties"].keys()
122
+ )
123
+
124
+ self.tools["function"] = {"type": "function", "function": user_function}
125
+ self.client.beta.assistants.update(
126
+ self._get_agent_id(), tools=list(self.tools.values())
127
+ )
128
+ return """
129
+ IMPORTANT: Always use the function 'response' tool to respond to the user with only the requested JSON schema. DO NOT add any text to the response outside of the JSON schema.
130
+
131
+ """
132
+
133
+ def _format_messages(self, messages):
134
+ message_list = []
135
+ if isinstance(messages, str):
136
+ message_list.insert(0, {"role": "user", "content": messages})
137
+ else:
138
+ for message in messages:
139
+ if isinstance(message, str):
140
+ message_list.insert(0, {"role": "user", "content": message})
141
+ else:
142
+ raise Exception(
143
+ f"==== Invalid message: {message} ====\nMust be a string "
144
+ )
145
+ return message_list
146
+
147
+ def generate_json(self, messages, function, additional_instructions=""):
148
+ # _instructions_addition = self._add_function(function)
149
+ function["strict"] = True
150
+ function["parameters"]["additionalProperties"] = False
151
+ function["parameters"]["required"] = list(
152
+ function["parameters"]["properties"].keys()
153
+ )
154
+
155
+ formatted_messages = self._format_messages(messages)
156
+ thread = self.client.beta.threads.create(messages=formatted_messages)
157
+ # log(function, _print=True)
158
+ running_job = True
159
+ while running_job:
160
+ try:
161
+ run = self.client.beta.threads.runs.create_and_poll(
162
+ thread_id=thread.id,
163
+ assistant_id=self._get_agent_id(),
164
+ additional_instructions=additional_instructions,
165
+ parallel_tool_calls=False,
166
+ tools=[
167
+ {"type": "file_search"},
168
+ {"type": "function", "function": function},
169
+ ],
170
+ tool_choice={
171
+ "type": "function",
172
+ "function": {"name": function["name"]},
173
+ },
174
+ )
175
+ log(f"==== Job Status: {run.status} ====", _print=True)
176
+ if run.status in [
177
+ "failed",
178
+ "expired",
179
+ "canceled",
180
+ "completed",
181
+ "incomplete",
182
+ "requires_action",
183
+ ]:
184
+ running_job = False
185
+
186
+ except openai.error.BadRequestError as e:
187
+ # Handle specific bad request errors
188
+ error_message = e.json_body.get("error", {}).get("message", "")
189
+ if "already has an active run" in error_message:
190
+ log("Previous run is still active. Waiting...", _print=True)
191
+ time.sleep(2) # wait before retrying or checking run status
192
+ else:
193
+ raise e
194
+
195
+ # while run.status in ["queued", "in_progress"]:
196
+ # run = self.client.beta.threads.runs.retrieve(
197
+ # thread_id=thread.id,
198
+ # run_id=run.id,
199
+ # )
200
+ # time.sleep(0.5)
201
+ if run.status in ["failed", "expired", "canceled"]:
202
+ log(f"==== !!! ERROR !!!: {run.last_error} ====", _print=True)
203
+ return None
204
+ log("=================== RUN COMPLETED ===================", _print=True)
205
+ log(run.status, _print=True)
206
+ if run.status == "completed":
207
+ response = self.client.beta.threads.messages.list(thread_id=thread.id)
208
+ results = response.data[0].content[0].text.value
209
+ elif run.status == "requires_action":
210
+ results = run.required_action.submit_tool_outputs.tool_calls[
211
+ 0
212
+ ].function.arguments
213
+ else:
214
+ log(f"====Status: {run.status} Error: {run.last_error} ====", _print=True)
215
+ return None
216
+
217
+ results = results[results.find("{") : results.rfind("}") + 1]
218
+ try:
219
+ results = json.loads(results, strict=False)
220
+ except Exception:
221
+ print(f"==== Invalid JSON:\n{results}", _print=True)
222
+ return {}
223
+ else:
224
+ log(f"==== Results: {results}", _print=True)
225
+ log("=================== END REPORT ===================", _print=True)
226
+ return results
227
+
228
+ def generate_text(self, messages, additional_instructions=""):
229
+ formatted_messages = self._format_messages(messages)
230
+ thread = self.client.beta.threads.create(messages=formatted_messages)
231
+
232
+ run = self.client.beta.threads.runs.create(
233
+ thread_id=thread.id,
234
+ assistant_id=self._get_agent_id(),
235
+ additional_instructions=additional_instructions,
236
+ parallel_tool_calls=False,
237
+ )
238
+
239
+ while run.status in ["queued", "in_progress"]:
240
+ run = self.client.beta.threads.runs.retrieve(
241
+ thread_id=thread.id,
242
+ run_id=run.id,
243
+ )
244
+ time.sleep(0.5)
245
+ log(f"==== Job Status: {run.status} ====", _print=True)
246
+
247
+ if run.status in ["failed", "expired", "canceled"]:
248
+ log(f"==== Error: {run.last_error} ====", _print=True)
249
+ return None
250
+ log("=================== RUN COMPLETED ===================", _print=True)
251
+ log(run.status, _print=True)
252
+ if run.status == "completed":
253
+ response = self.client.beta.threads.messages.list(thread_id=thread.id)
254
+ results = response.data[0].content[0].text.value
255
+ else:
256
+ log(f"====Status: {run.status} Error: {run.last_error} ====", _print=True)
257
+ return None
258
+
259
+ log(results, _print=True)
260
+ log("=================== END REPORT ===================", _print=True)
261
+ return results
262
+
263
+ def generate_audio(self, prompt, file_path, **kwargs):
264
+ voice = kwargs.get("voice") or random.choice(
265
+ ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
266
+ )
267
+ response = self.client.audio.speech.create(
268
+ model="tts-1",
269
+ voice=voice,
270
+ input=prompt,
271
+ )
272
+
273
+ return response.stream_to_file(file_path)
274
+
275
+ def generate_image(self, prompt, **kwargs):
276
+ image = None
277
+ try:
278
+ response = self.client.images.generate(
279
+ model=self._image_model,
280
+ prompt=prompt,
281
+ response_format="b64_json",
282
+ **kwargs,
283
+ )
284
+ image_dict = response.data[0]
285
+ except Exception as e:
286
+ print(f"==== Error: Unable to create image ====\n\n{e}")
287
+ else:
288
+ image = b64decode(image_dict.b64_json)
289
+ return image
290
+
291
+ def summarize_text(self, text, primer=""):
292
+ message = [
293
+ {
294
+ "role": "system",
295
+ "content": f"You are a highly skilled AI trained in language comprehension and summarization.{primer}",
296
+ },
297
+ {"role": "user", "content": text},
298
+ ]
299
+ response = self.client.chat.completions.create(
300
+ model=self._text_model, messages=message
301
+ )
302
+ try:
303
+ result = response.choices[0].message.content
304
+ except Exception as e:
305
+ log(f"{type(e)}:{e}\n\n Unable to generate content ====")
306
+ return None
307
+
308
+ return result
@@ -0,0 +1,40 @@
1
+ from autonomous import log
2
+ from autonomous.model.autoattr import ReferenceAttr
3
+ from autonomous.model.automodel import AutoModel
4
+
5
+ from .models.openai import OpenAIModel
6
+
7
+
8
+ class OAIAgent(AutoModel):
9
+ client_model = ReferenceAttr(choices=[OpenAIModel])
10
+ _ai_model = OpenAIModel
11
+
12
+ @property
13
+ def client(self):
14
+ if self.client_model is None:
15
+ self.client_model = self._ai_model()
16
+ self.save()
17
+ return self.client_model
18
+
19
+ def clear_files(self, file_id=None):
20
+ return self.client.clear_files(file_id)
21
+
22
+ def attach_file(self, file_contents, filename="dbdata.json"):
23
+ return self.client.attach_file(file_contents, filename)
24
+
25
+ def generate(self, messages, function=None, additional_instructions=""):
26
+ if function is None:
27
+ return self.client.generate_text(messages, additional_instructions)
28
+ else:
29
+ return self.client.generate_json(
30
+ messages, function, additional_instructions
31
+ )
32
+
33
+ def generate_audio(self, prompt, file_path, **kwargs):
34
+ return self.client.generate_audio(self, file_path, **kwargs)
35
+
36
+ def generate_image(self, prompt, **kwargs):
37
+ return self.client.generate_image(prompt, **kwargs)
38
+
39
+ def summarize_text(self, text, primer=""):
40
+ return self.client.generate_image(text, primer)
@@ -0,0 +1,35 @@
1
+ from autonomous import log
2
+ from autonomous.model.autoattr import ReferenceAttr, StringAttr
3
+ from autonomous.model.automodel import AutoModel
4
+
5
+ from .models.openai import OpenAIModel
6
+
7
+
8
+ class TextAgent(AutoModel):
9
+ client = ReferenceAttr(choices=[OpenAIModel])
10
+ name = StringAttr(default="textagent")
11
+ instructions = StringAttr(
12
+ default="You are highly skilled AI trained to assist with generating text according to the given requirements."
13
+ )
14
+ description = StringAttr(
15
+ default="A helpful AI assistant trained to assist with generating text according to the given requirements."
16
+ )
17
+
18
+ _ai_model = OpenAIModel
19
+
20
+ def get_client(self):
21
+ if self.client is None:
22
+ self.client = self._ai_model(
23
+ name=self.name,
24
+ instructions=self.instructions,
25
+ description=self.description,
26
+ )
27
+ self.client.save()
28
+ self.save()
29
+ return self.client
30
+
31
+ def summarize_text(self, text, primer=""):
32
+ return self.get_client().summarize_text(text, primer)
33
+
34
+ def generate(self, messages, additional_instructions=""):
35
+ return self.get_client().generate_text(messages, additional_instructions)
@@ -1,17 +1,18 @@
1
+ import json
1
2
  import uuid
2
3
  from datetime import datetime
3
4
  from functools import wraps
4
5
 
5
6
  import requests
6
7
  from authlib.integrations.requests_client import OAuth2Auth, OAuth2Session
7
- from flask import current_app, redirect, request, session, url_for
8
+ from flask import redirect, session, url_for
8
9
 
9
10
  from autonomous import log
10
- from autonomous.auth.user import AutoUser
11
+ from autonomous.auth.user import User
11
12
 
12
13
 
13
14
  class AutoAuth:
14
- user_class = AutoUser
15
+ user_class: type[User] = User
15
16
 
16
17
  def __init__(
17
18
  self,
@@ -46,13 +47,12 @@ class AutoAuth:
46
47
  """
47
48
  Returns the current user.
48
49
  """
49
- user = cls.user_class.get_guest()
50
- if session.get("user") and session["user"]["state"] == "authenticated":
51
- try:
52
- user = cls.user_class.get(session["user"].get("pk"))
53
- user.pk
54
- except Exception as e:
55
- log(e, session["user"])
50
+
51
+ user = (
52
+ cls.user_class.from_json(session["user"]) if session.get("user") else None
53
+ )
54
+ if not user or user.state != "authenticated":
55
+ user = cls.user_class.get_guest()
56
56
  return user
57
57
 
58
58
  def authenticate(self):
@@ -106,7 +106,7 @@ class AutoAuth:
106
106
  user.last_login = datetime.now()
107
107
  # log(user)
108
108
  user.save()
109
- session["user"] = user.serialize()
109
+ session["user"] = user.to_json()
110
110
  # log(guest, user.is_guest)
111
111
  if not guest and user.is_guest:
112
112
  return redirect(url_for("auth.login"))