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.
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/PKG-INFO +7 -8
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/pyproject.toml +3 -3
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/requirements.txt +4 -9
- autonomous_app-0.3.1/src/autonomous/__init__.py +7 -0
- autonomous_app-0.3.1/src/autonomous/ai/audioagent.py +32 -0
- autonomous_app-0.3.1/src/autonomous/ai/imageagent.py +31 -0
- autonomous_app-0.3.1/src/autonomous/ai/jsonagent.py +40 -0
- autonomous_app-0.3.1/src/autonomous/ai/models/openai.py +308 -0
- autonomous_app-0.3.1/src/autonomous/ai/oaiagent.py +40 -0
- autonomous_app-0.3.1/src/autonomous/ai/textagent.py +35 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/autoauth.py +11 -11
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/user.py +24 -11
- autonomous_app-0.3.1/src/autonomous/db/__init__.py +42 -0
- autonomous_app-0.3.1/src/autonomous/db/base/__init__.py +33 -0
- autonomous_app-0.3.1/src/autonomous/db/base/common.py +62 -0
- autonomous_app-0.3.1/src/autonomous/db/base/datastructures.py +476 -0
- autonomous_app-0.3.1/src/autonomous/db/base/document.py +1230 -0
- autonomous_app-0.3.1/src/autonomous/db/base/fields.py +767 -0
- autonomous_app-0.3.1/src/autonomous/db/base/metaclasses.py +468 -0
- autonomous_app-0.3.1/src/autonomous/db/base/utils.py +22 -0
- autonomous_app-0.3.1/src/autonomous/db/common.py +79 -0
- autonomous_app-0.3.1/src/autonomous/db/connection.py +472 -0
- autonomous_app-0.3.1/src/autonomous/db/context_managers.py +313 -0
- autonomous_app-0.3.1/src/autonomous/db/dereference.py +291 -0
- autonomous_app-0.3.1/src/autonomous/db/document.py +1141 -0
- autonomous_app-0.3.1/src/autonomous/db/errors.py +165 -0
- autonomous_app-0.3.1/src/autonomous/db/fields.py +2732 -0
- autonomous_app-0.3.1/src/autonomous/db/mongodb_support.py +24 -0
- autonomous_app-0.3.1/src/autonomous/db/pymongo_support.py +80 -0
- autonomous_app-0.3.1/src/autonomous/db/queryset/__init__.py +28 -0
- autonomous_app-0.3.1/src/autonomous/db/queryset/base.py +2033 -0
- autonomous_app-0.3.1/src/autonomous/db/queryset/field_list.py +88 -0
- autonomous_app-0.3.1/src/autonomous/db/queryset/manager.py +58 -0
- autonomous_app-0.3.1/src/autonomous/db/queryset/queryset.py +189 -0
- autonomous_app-0.3.1/src/autonomous/db/queryset/transform.py +527 -0
- autonomous_app-0.3.1/src/autonomous/db/queryset/visitor.py +189 -0
- autonomous_app-0.3.1/src/autonomous/db/signals.py +59 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/logger.py +3 -0
- autonomous_app-0.3.1/src/autonomous/model/autoattr.py +120 -0
- autonomous_app-0.3.1/src/autonomous/model/automodel.py +213 -0
- autonomous_app-0.3.1/src/autonomous/storage/__init__.py +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/storage/imagestorage.py +9 -54
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/tasks/autotask.py +0 -25
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous_app.egg-info/PKG-INFO +7 -8
- autonomous_app-0.3.1/src/autonomous_app.egg-info/SOURCES.txt +65 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous_app.egg-info/requires.txt +4 -5
- autonomous_app-0.2.25/src/autonomous/__init__.py +0 -4
- autonomous_app-0.2.25/src/autonomous/ai/oaiagent.py +0 -214
- autonomous_app-0.2.25/src/autonomous/db/autodb.py +0 -86
- autonomous_app-0.2.25/src/autonomous/db/table.py +0 -156
- autonomous_app-0.2.25/src/autonomous/errors/__init__.py +0 -1
- autonomous_app-0.2.25/src/autonomous/errors/danglingreferenceerror.py +0 -8
- autonomous_app-0.2.25/src/autonomous/model/__init__.py +0 -1
- autonomous_app-0.2.25/src/autonomous/model/autoattribute.py +0 -20
- autonomous_app-0.2.25/src/autonomous/model/automodel.py +0 -400
- autonomous_app-0.2.25/src/autonomous/model/orm.py +0 -86
- autonomous_app-0.2.25/src/autonomous/model/serializer.py +0 -110
- autonomous_app-0.2.25/src/autonomous_app.egg-info/SOURCES.txt +0 -41
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/LICENSE +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/README.md +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/setup.cfg +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/setup.py +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/ai/__init__.py +0 -0
- {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/ai/models}/__init__.py +0 -0
- {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/GHCallbacks.py +0 -0
- {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/GHOrganization.py +0 -0
- {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/GHRepo.py +0 -0
- {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/GHVersionControl.py +0 -0
- {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/apis}/version_control/__init__.py +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/__init__.py +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/github.py +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/auth/google.py +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/cli.py +0 -0
- {autonomous_app-0.2.25/src/autonomous/db → autonomous_app-0.3.1/src/autonomous/model}/__init__.py +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/storage/localstorage.py +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous/tasks/__init__.py +0 -0
- {autonomous_app-0.2.25/src/autonomous/storage → autonomous_app-0.3.1/src/autonomous/utils}/markdown.py +0 -0
- {autonomous_app-0.2.25 → autonomous_app-0.3.1}/src/autonomous_app.egg-info/dependency_links.txt +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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:
|
|
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:
|
|
48
|
-
Requires-Dist:
|
|
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.
|
|
13
|
+
requires-python = ">=3.12"
|
|
14
14
|
license={file = "LICENSE"}
|
|
15
15
|
classifiers=[
|
|
16
|
-
"Programming Language :: Python :: 3.
|
|
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
|
-
|
|
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.
|
|
42
|
+
openai >= 1.42
|
|
46
43
|
|
|
47
44
|
##### Parsing #####
|
|
48
|
-
|
|
49
|
-
validators
|
|
50
45
|
dateparser
|
|
51
46
|
python-slugify
|
|
@@ -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
|
|
8
|
+
from flask import redirect, session, url_for
|
|
8
9
|
|
|
9
10
|
from autonomous import log
|
|
10
|
-
from autonomous.auth.user import
|
|
11
|
+
from autonomous.auth.user import User
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class AutoAuth:
|
|
14
|
-
user_class =
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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.
|
|
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"))
|