Settings prototype
Settings modal window managed from python - work in progress
frdel committed
Oct 27, 2024 at 18:04 UTC
a5d671904d4deebf147adc2b728bdc5b36627144
9 files changed
+976
-50
initialize.py
+9
-6
@@ -1,11 +1,11 @@
1
import models
2
from agent import AgentConfig
3
-from python.helpers import files
3
+from python.helpers import files, settings
4
5
def initialize():
6
7
# main chat model used by agents (smarter, more accurate)
8
- chat_llm = models.get_openai_chat(model_name="gpt-4o-mini", temperature=0)
8
+ # chat_llm = models.get_openai_chat(model_name="gpt-4o-mini", temperature=0)
9
# chat_llm = models.get_ollama_chat(model_name="llama3.2:3b-instruct-fp16", temperature=0)
10
# chat_llm = models.get_lmstudio_chat(model_name="lmstudio-community/Meta-Llama-3.1-8B-Instruct-GGUF", temperature=0)
11
# chat_llm = models.get_openrouter_chat(model_name="openai/o1-mini-2024-09-12")
@@ -15,22 +15,25 @@ def initialize():
15
# chat_llm = models.get_mistral_chat(model_name="mistral-small-latest", temperature=0)
16
# chat_llm = models.get_groq_chat(model_name="llama-3.2-90b-text-preview", temperature=0)
17
# chat_llm = models.get_sambanova_chat(model_name="Meta-Llama-3.1-70B-Instruct-8k", temperature=0)
18
+ chat_llm = settings.get_chat_model() # chat model from user settings
19
20
# utility model used for helper functions (cheaper, faster)
20
- utility_llm = chat_llm
21
+ # utility_llm = chat_llm
22
+ utility_llm = settings.get_utility_model() # utility model from user settings
23
24
# embedding model used for memory
23
- embedding_llm = models.get_openai_embedding(model_name="text-embedding-3-small")
25
+ # embedding_llm = models.get_openai_embedding(model_name="text-embedding-3-small")
26
# embedding_llm = models.get_ollama_embedding(model_name="nomic-embed-text")
27
# embedding_llm = models.get_huggingface_embedding(model_name="sentence-transformers/all-MiniLM-L6-v2")
28
# embedding_llm = models.get_lmstudio_embedding(model_name="nomic-ai/nomic-embed-text-v1.5-GGUF")
27
-
29
+ embedding_llm = settings.get_embedding_model() # embedding model from user settings
30
+
31
# agent configuration
32
config = AgentConfig(
33
chat_model = chat_llm,
34
utility_model = utility_llm,
35
embeddings_model = embedding_llm,
33
- # prompts_subdir = "default",
36
+ prompts_subdir = "dianoia-xl",
37
# memory_subdir = "",
38
knowledge_subdirs = ["default","custom"],
39
auto_memory_count = 0,
models.py
+202
-42
@@ -1,5 +1,13 @@
1
+from enum import Enum
2
import os
2
-from langchain_openai import ChatOpenAI, OpenAI, OpenAIEmbeddings, AzureChatOpenAI, AzureOpenAIEmbeddings, AzureOpenAI
3
+from langchain_openai import (
4
+ ChatOpenAI,
5
+ OpenAI,
6
+ OpenAIEmbeddings,
7
+ AzureChatOpenAI,
8
+ AzureOpenAIEmbeddings,
9
+ AzureOpenAI,
10
+)
11
from langchain_community.llms.ollama import Ollama
12
from langchain_ollama import ChatOllama
13
from langchain_community.embeddings import OllamaEmbeddings
@@ -17,74 +25,226 @@ load_dotenv()
25
# Configuration
26
DEFAULT_TEMPERATURE = 0.0
27
28
+
29
+class ModelType(Enum):
30
+ CHAT = "Chat"
31
+ EMBEDDING = "Embedding"
32
+
33
+
34
+class ModelProvider(Enum):
35
+ ANTHROPIC = "Anthropic"
36
+ HUGGINGFACE = "HuggingFace"
37
+ GOOGLE = "Google"
38
+ GROQ = "Groq"
39
+ LMSTUDIO = "LM Studio"
40
+ MISTRALAI = "Mistral AI"
41
+ OLLAMA = "Ollama"
42
+ OPENAI = "OpenAI"
43
+ OPENAI_AZURE = "OpenAI Azure"
44
+ OPENROUTER = "OpenRouter"
45
+ SAMBANOVA = "Sambanova"
46
+
47
+
48
# Utility function to get API keys from environment variables
49
def get_api_key(service):
22
- return os.getenv(f"API_KEY_{service.upper()}") or os.getenv(f"{service.upper()}_API_KEY")
50
+ return os.getenv(f"API_KEY_{service.upper()}") or os.getenv(
51
+ f"{service.upper()}_API_KEY"
52
+ )
53
+
54
+
55
+def get_model(type: ModelType, provider: ModelProvider, name: str, **kwargs):
56
+ fnc_name = f"get_{provider.name.lower()}_{type.name.lower()}" # function name of model getter
57
+ model = globals()[fnc_name](name, **kwargs) # call function by name
58
+ return model
59
60
61
# Ollama models
26
-def get_ollama_chat(model_name:str, temperature=DEFAULT_TEMPERATURE, base_url=os.getenv("OLLAMA_BASE_URL") or "http://127.0.0.1:11434", num_ctx=8192):
27
- return ChatOllama(model=model_name,temperature=temperature, base_url=base_url, num_ctx=num_ctx)
62
+def get_ollama_chat(
63
+ model_name: str,
64
+ temperature=DEFAULT_TEMPERATURE,
65
+ base_url=os.getenv("OLLAMA_BASE_URL") or "http://127.0.0.1:11434",
66
+ num_ctx=8192,
67
+ **kwargs,
68
+):
69
+ return ChatOllama(
70
+ model=model_name,
71
+ temperature=temperature,
72
+ base_url=base_url,
73
+ num_ctx=num_ctx,
74
+ **kwargs,
75
+ )
76
+
77
+
78
+def get_ollama_embedding(
79
+ model_name: str,
80
+ temperature=DEFAULT_TEMPERATURE,
81
+ base_url=os.getenv("OLLAMA_BASE_URL") or "http://127.0.0.1:11434",
82
+ **kwargs,
83
+):
84
+ return OllamaEmbeddings(
85
+ model=model_name, temperature=temperature, base_url=base_url, **kwargs
86
+ )
87
29
-def get_ollama_embedding(model_name:str, temperature=DEFAULT_TEMPERATURE, base_url=os.getenv("OLLAMA_BASE_URL") or "http://127.0.0.1:11434"):
30
-
31
- return OllamaEmbeddings(model=model_name,temperature=temperature, base_url=base_url)
88
89
# HuggingFace models
90
35
-def get_huggingface_embedding(model_name:str):
36
- return HuggingFaceEmbeddings(model_name=model_name)
91
+
92
+def get_huggingface_embedding(model_name: str, **kwargs):
93
+ return HuggingFaceEmbeddings(model_name=model_name, **kwargs)
94
+
95
96
# LM Studio and other OpenAI compatible interfaces
39
-def get_lmstudio_chat(model_name:str, temperature=DEFAULT_TEMPERATURE, base_url=os.getenv("LM_STUDIO_BASE_URL") or "http://127.0.0.1:1234/v1"):
40
- return ChatOpenAI(model_name=model_name, base_url=base_url, temperature=temperature, api_key="none") # type: ignore
97
+def get_lmstudio_chat(
98
+ model_name: str,
99
+ temperature=DEFAULT_TEMPERATURE,
100
+ base_url=os.getenv("LM_STUDIO_BASE_URL") or "http://127.0.0.1:1234/v1",
101
+ **kwargs,
102
+):
103
+ return ChatOpenAI(model_name=model_name, base_url=base_url, temperature=temperature, api_key="none", **kwargs) # type: ignore
104
+
105
+
106
+def get_lmstudio_embedding(
107
+ model_name: str,
108
+ base_url=os.getenv("LM_STUDIO_BASE_URL") or "http://127.0.0.1:1234/v1",
109
+ **kwargs,
110
+):
111
+ return OpenAIEmbeddings(model=model_name, api_key="none", base_url=base_url, check_embedding_ctx_length=False, **kwargs) # type: ignore
112
42
-def get_lmstudio_embedding(model_name:str, base_url=os.getenv("LM_STUDIO_BASE_URL") or "http://127.0.0.1:1234/v1"):
43
- return OpenAIEmbeddings(model=model_name, api_key="none", base_url=base_url, check_embedding_ctx_length=False) # type: ignore
113
114
# Anthropic models
46
-def get_anthropic_chat(model_name:str, api_key=get_api_key("anthropic"), temperature=DEFAULT_TEMPERATURE):
47
- return ChatAnthropic(model_name=model_name, temperature=temperature, api_key=api_key) # type: ignore
115
+def get_anthropic_chat(
116
+ model_name: str,
117
+ api_key=get_api_key("anthropic"),
118
+ temperature=DEFAULT_TEMPERATURE,
119
+ **kwargs,
120
+):
121
+ return ChatAnthropic(model_name=model_name, temperature=temperature, api_key=api_key, **kwargs) # type: ignore
122
+
123
124
# OpenAI models
50
-def get_openai_chat(model_name:str, api_key=get_api_key("openai"), temperature=DEFAULT_TEMPERATURE):
51
- return ChatOpenAI(model_name=model_name, temperature=temperature, api_key=api_key) # type: ignore
125
+def get_openai_chat(
126
+ model_name: str,
127
+ api_key=get_api_key("openai"),
128
+ temperature=DEFAULT_TEMPERATURE,
129
+ **kwargs,
130
+):
131
+ return ChatOpenAI(model_name=model_name, temperature=temperature, api_key=api_key, **kwargs) # type: ignore
132
+
133
53
-def get_openai_instruct(model_name:str, api_key=get_api_key("openai"), temperature=DEFAULT_TEMPERATURE):
54
- return OpenAI(model=model_name, temperature=temperature, api_key=api_key) # type: ignore
134
+def get_openai_instruct(
135
+ model_name: str,
136
+ api_key=get_api_key("openai"),
137
+ temperature=DEFAULT_TEMPERATURE,
138
+ **kwargs,
139
+):
140
+ return OpenAI(model=model_name, temperature=temperature, api_key=api_key, **kwargs) # type: ignore
141
56
-def get_openai_embedding(model_name:str, api_key=get_api_key("openai")):
57
- return OpenAIEmbeddings(model=model_name, api_key=api_key) # type: ignore
142
59
-def get_azure_openai_chat(deployment_name:str, api_key=get_api_key("openai_azure"), temperature=DEFAULT_TEMPERATURE, azure_endpoint=os.getenv("OPENAI_AZURE_ENDPOINT")):
60
- return AzureChatOpenAI(deployment_name=deployment_name, temperature=temperature, api_key=api_key, azure_endpoint=azure_endpoint) # type: ignore
143
+def get_openai_embedding(model_name: str, api_key=get_api_key("openai"), **kwargs):
144
+ return OpenAIEmbeddings(model=model_name, api_key=api_key, **kwargs) # type: ignore
145
62
-def get_azure_openai_instruct(deployment_name:str, api_key=get_api_key("openai_azure"), temperature=DEFAULT_TEMPERATURE, azure_endpoint=os.getenv("OPENAI_AZURE_ENDPOINT")):
63
- return AzureOpenAI(deployment_name=deployment_name, temperature=temperature, api_key=api_key, azure_endpoint=azure_endpoint) # type: ignore
146
65
-def get_azure_openai_embedding(deployment_name:str, api_key=get_api_key("openai_azure"), azure_endpoint=os.getenv("OPENAI_AZURE_ENDPOINT")):
66
- return AzureOpenAIEmbeddings(deployment_name=deployment_name, api_key=api_key, azure_endpoint=azure_endpoint) # type: ignore
147
+def get_azure_openai_chat(
148
+ deployment_name: str,
149
+ api_key=get_api_key("openai_azure"),
150
+ temperature=DEFAULT_TEMPERATURE,
151
+ azure_endpoint=os.getenv("OPENAI_AZURE_ENDPOINT"),
152
+ **kwargs,
153
+):
154
+ return AzureChatOpenAI(deployment_name=deployment_name, temperature=temperature, api_key=api_key, azure_endpoint=azure_endpoint, **kwargs) # type: ignore
155
+
156
+
157
+def get_azure_openai_instruct(
158
+ deployment_name: str,
159
+ api_key=get_api_key("openai_azure"),
160
+ temperature=DEFAULT_TEMPERATURE,
161
+ azure_endpoint=os.getenv("OPENAI_AZURE_ENDPOINT"),
162
+ **kwargs,
163
+):
164
+ return AzureOpenAI(deployment_name=deployment_name, temperature=temperature, api_key=api_key, azure_endpoint=azure_endpoint, **kwargs) # type: ignore
165
+
166
+
167
+def get_azure_openai_embedding(
168
+ deployment_name: str,
169
+ api_key=get_api_key("openai_azure"),
170
+ azure_endpoint=os.getenv("OPENAI_AZURE_ENDPOINT"),
171
+ **kwargs,
172
+):
173
+ return AzureOpenAIEmbeddings(deployment_name=deployment_name, api_key=api_key, azure_endpoint=azure_endpoint, **kwargs) # type: ignore
174
+
175
176
# Google models
69
-def get_google_chat(model_name:str, api_key=get_api_key("google"), temperature=DEFAULT_TEMPERATURE):
70
- return GoogleGenerativeAI(model=model_name, temperature=temperature, google_api_key=api_key, safety_settings={HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE }) # type: ignore
177
+def get_google_chat(
178
+ model_name: str,
179
+ api_key=get_api_key("google"),
180
+ temperature=DEFAULT_TEMPERATURE,
181
+ **kwargs,
182
+):
183
+ return GoogleGenerativeAI(model=model_name, temperature=temperature, google_api_key=api_key, safety_settings={HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_NONE}, **kwargs) # type: ignore
184
+
185
186
# Mistral models
73
-def get_mistral_chat(model_name:str, api_key=get_api_key("mistral"), temperature=DEFAULT_TEMPERATURE):
74
- return ChatMistralAI(model=model_name, temperature=temperature, api_key=api_key) # type: ignore
187
+def get_mistral_chat(
188
+ model_name: str,
189
+ api_key=get_api_key("mistral"),
190
+ temperature=DEFAULT_TEMPERATURE,
191
+ **kwargs,
192
+):
193
+ return ChatMistralAI(model=model_name, temperature=temperature, api_key=api_key, **kwargs) # type: ignore
194
+
195
196
# Groq models
77
-def get_groq_chat(model_name:str, api_key=get_api_key("groq"), temperature=DEFAULT_TEMPERATURE):
78
- return ChatGroq(model_name=model_name, temperature=temperature, api_key=api_key) # type: ignore
79
-
197
+def get_groq_chat(
198
+ model_name: str,
199
+ api_key=get_api_key("groq"),
200
+ temperature=DEFAULT_TEMPERATURE,
201
+ **kwargs,
202
+):
203
+ return ChatGroq(model_name=model_name, temperature=temperature, api_key=api_key, **kwargs) # type: ignore
204
+
205
+
206
# OpenRouter models
81
-def get_openrouter_chat(model_name: str, api_key=get_api_key("openrouter"), temperature=DEFAULT_TEMPERATURE, base_url=os.getenv("OPEN_ROUTER_BASE_URL") or "https://openrouter.ai/api/v1"):
82
- return ChatOpenAI(api_key=api_key, model=model_name, temperature=temperature, base_url=base_url) # type: ignore
83
-
84
-def get_openrouter_embedding(model_name: str, api_key=get_api_key("openrouter"), base_url=os.getenv("OPEN_ROUTER_BASE_URL") or "https://openrouter.ai/api/v1"):
85
- return OpenAIEmbeddings(model=model_name, api_key=api_key, base_url=base_url) # type: ignore
207
+def get_openrouter_chat(
208
+ model_name: str,
209
+ api_key=get_api_key("openrouter"),
210
+ temperature=DEFAULT_TEMPERATURE,
211
+ base_url=os.getenv("OPEN_ROUTER_BASE_URL") or "https://openrouter.ai/api/v1",
212
+ **kwargs,
213
+):
214
+ return ChatOpenAI(api_key=api_key, model=model_name, temperature=temperature, base_url=base_url, **kwargs) # type: ignore
215
+
216
+
217
+def get_openrouter_embedding(
218
+ model_name: str,
219
+ api_key=get_api_key("openrouter"),
220
+ base_url=os.getenv("OPEN_ROUTER_BASE_URL") or "https://openrouter.ai/api/v1",
221
+ **kwargs,
222
+):
223
+ return OpenAIEmbeddings(model=model_name, api_key=api_key, base_url=base_url, **kwargs) # type: ignore
224
+
225
226
# Sambanova models
88
-def get_sambanova_chat(model_name: str, api_key=get_api_key("sambanova"), temperature=DEFAULT_TEMPERATURE, base_url=os.getenv("SAMBANOVA_BASE_URL") or "https://fast-api.snova.ai/v1", max_tokens=1024):
89
- return ChatOpenAI(api_key=api_key, model=model_name, temperature=temperature, base_url=base_url, max_tokens=max_tokens) # type: ignore
90
-
227
+def get_sambanova_chat(
228
+ model_name: str,
229
+ api_key=get_api_key("sambanova"),
230
+ temperature=DEFAULT_TEMPERATURE,
231
+ base_url=os.getenv("SAMBANOVA_BASE_URL") or "https://fast-api.snova.ai/v1",
232
+ max_tokens=1024,
233
+ **kwargs,
234
+):
235
+ return ChatOpenAI(api_key=api_key, model=model_name, temperature=temperature, base_url=base_url, max_tokens=max_tokens, **kwargs) # type: ignore
236
+
237
+
238
+# Other OpenAI compatible models
239
+def get_other_chat(
240
+ model_name: str,
241
+ api_key=None,
242
+ temperature=DEFAULT_TEMPERATURE,
243
+ base_url=None,
244
+ **kwargs,
245
+):
246
+ return ChatOpenAI(api_key=api_key, model=model_name, temperature=temperature, base_url=base_url, **kwargs) # type: ignore
247
+
248
+
249
+def get_other_embedding(model_name: str, api_key=None, base_url=None, **kwargs):
250
+ return OpenAIEmbeddings(model=model_name, api_key=api_key, base_url=base_url, **kwargs) # type: ignore
python/helpers/settings.py
new
+273
@@ -0,0 +1,273 @@
1
+import json
2
+import os
3
+import re
4
+from typing import Any, Optional, TypedDict
5
+from . import files
6
+from models import get_model, ModelProvider, ModelType
7
+from langchain_core.language_models.chat_models import BaseChatModel
8
+from langchain_core.embeddings import Embeddings
9
+
10
+class Settings(TypedDict):
11
+ chat_model_provider: str
12
+ chat_model_name: str
13
+ chat_model_temperature: float
14
+ chat_model_kwargs: dict[str, str]
15
+
16
+ util_model_provider: str
17
+ util_model_name: str
18
+ util_model_temperature: float
19
+ util_model_kwargs: dict[str, str]
20
+
21
+ embed_model_provider: str
22
+ embed_model_name: str
23
+ embed_model_temperature: float
24
+ embed_model_kwargs: dict[str, str]
25
+
26
+
27
+class PartialSettings(Settings, total=False):
28
+ pass
29
+
30
+
31
+SETTINGS_FILE = files.get_abs_path("tmp/settings.json")
32
+_settings: Settings | None = None
33
+
34
+
35
+def convert_out(settings: Settings) -> dict[str, Any]:
36
+
37
+ # main model section
38
+ chat_model_fields = []
39
+ chat_model_fields.append(
40
+ {
41
+ "id": "chat_model_provider",
42
+ "title": "Chat model provider",
43
+ "description": "Select provider for main chat model used by Agent Zero",
44
+ "type": "select",
45
+ "value": settings["chat_model_provider"],
46
+ "options": [{"value": p.name, "label": p.value} for p in ModelProvider],
47
+ }
48
+ )
49
+ chat_model_fields.append(
50
+ {
51
+ "id": "chat_model_name",
52
+ "title": "Chat model name",
53
+ "description": "Exact name of model from selected provider",
54
+ "type": "input",
55
+ "value": settings["chat_model_name"],
56
+ }
57
+ )
58
+
59
+ chat_model_fields.append(
60
+ {
61
+ "id": "chat_model_temperature",
62
+ "title": "Chat model temperature",
63
+ "description": "Determines the randomness of generated responses. 0 is deterministic, 1 is random",
64
+ "type": "range",
65
+ "min": 0,
66
+ "max": 1,
67
+ "step": 0.01,
68
+ "value": settings["chat_model_temperature"],
69
+ }
70
+ )
71
+
72
+ chat_model_fields.append(
73
+ {
74
+ "id": "chat_model_kwargs",
75
+ "title": "Chat model additional parameters",
76
+ "description": "Any other parameters supported by the model. Format is KEY=VALUE on individual lines, just like .env file.",
77
+ "type": "textarea",
78
+ "value": _dict_to_env(settings["chat_model_kwargs"]),
79
+ }
80
+ )
81
+
82
+ chat_model_seciton = {
83
+ "title": "Chat Model",
84
+ "description": "Selection and settings for main chat model used by Agent Zero",
85
+ "fields": chat_model_fields,
86
+ }
87
+
88
+ # main model section
89
+ util_model_fields = []
90
+ util_model_fields.append(
91
+ {
92
+ "id": "util_model_provider",
93
+ "title": "Utility model provider",
94
+ "description": "Select provider for utility model used by the framework",
95
+ "type": "select",
96
+ "value": settings["util_model_provider"],
97
+ "options": [{"value": p.name, "label": p.value} for p in ModelProvider],
98
+ }
99
+ )
100
+ util_model_fields.append(
101
+ {
102
+ "id": "util_model_name",
103
+ "title": "Utility model name",
104
+ "description": "Exact name of model from selected provider",
105
+ "type": "input",
106
+ "value": settings["util_model_name"],
107
+ }
108
+ )
109
+
110
+ util_model_fields.append(
111
+ {
112
+ "id": "util_model_temperature",
113
+ "title": "Utility model temperature",
114
+ "description": "Determines the randomness of generated responses. 0 is deterministic, 1 is random",
115
+ "type": "range",
116
+ "min": 0,
117
+ "max": 1,
118
+ "step": 0.01,
119
+ "value": settings["util_model_temperature"],
120
+ }
121
+ )
122
+
123
+ util_model_fields.append(
124
+ {
125
+ "id": "util_model_kwargs",
126
+ "title": "Utility model additional parameters",
127
+ "description": "Any other parameters supported by the model. Format is KEY=VALUE on individual lines, just like .env file.",
128
+ "type": "textarea",
129
+ "value": _dict_to_env(settings["util_model_kwargs"]),
130
+ }
131
+ )
132
+
133
+ util_model_seciton = {
134
+ "title": "Utility model",
135
+ "description": "Smaller, cheaper, faster model for handling utility tasks like organizing memory, preparing prompts, summarizing.",
136
+ "fields": util_model_fields,
137
+ }
138
+
139
+ result = {"sections": [chat_model_seciton, util_model_seciton]}
140
+ return result
141
+
142
+def convert_in(settings: dict[str, Any]) -> Settings:
143
+ current = get_settings()
144
+ for section in settings["sections"]:
145
+ for field in section["fields"]:
146
+ if field["id"].endswith("_kwargs"):
147
+ current[field["id"]] = _env_to_dict(field["value"]) #parse KWARGS from env format
148
+ else:
149
+ current[field["id"]] = field["value"]
150
+ return current
151
+
152
+
153
+def get_settings() -> Settings:
154
+ global _settings
155
+ if not _settings:
156
+ _settings = _read_settings_file()
157
+ if not _settings:
158
+ _settings = _get_default_settings()
159
+ return _settings.copy()
160
+
161
+
162
+def set_settings(settings: Settings):
163
+ global _settings
164
+ _settings = normalize_settings(settings)
165
+ _apply_settings()
166
+ _write_settings_file(_settings)
167
+
168
+
169
+def normalize_settings(settings: Settings) -> Settings:
170
+ copy = settings.copy()
171
+ default = _get_default_settings()
172
+ for key, value in default.items():
173
+ if key not in copy:
174
+ copy[key] = value
175
+ return copy
176
+
177
+
178
+def get_chat_model() -> BaseChatModel:
179
+ settings = get_settings()
180
+ return get_model(
181
+ type=ModelType.CHAT,
182
+ provider=ModelProvider[settings["chat_model_provider"]],
183
+ name=settings["chat_model_name"],
184
+ temperature=settings["chat_model_temperature"],
185
+ **settings["chat_model_kwargs"],
186
+ )
187
+
188
+
189
+def get_utility_model() -> BaseChatModel:
190
+ settings = get_settings()
191
+ return get_model(
192
+ type=ModelType.CHAT,
193
+ provider=ModelProvider[settings["util_model_provider"]],
194
+ name=settings["util_model_name"],
195
+ temperature=settings["util_model_temperature"],
196
+ **settings["util_model_kwargs"],
197
+ )
198
+
199
+
200
+def get_embedding_model() -> Embeddings:
201
+ settings = get_settings()
202
+ return get_model(
203
+ type=ModelType.EMBEDDING,
204
+ provider=ModelProvider[settings["embed_model_provider"]],
205
+ name=settings["embed_model_name"],
206
+ temperature=settings["embed_model_temperature"],
207
+ **settings["embed_model_kwargs"],
208
+ )
209
+
210
+
211
+def _read_settings_file() -> Settings | None:
212
+ if os.path.exists(SETTINGS_FILE):
213
+ content = files.read_file(SETTINGS_FILE)
214
+ parsed = json.loads(content)
215
+ return normalize_settings(parsed)
216
+
217
+
218
+def _write_settings_file(settings: Settings):
219
+ content = json.dumps(settings, indent=4)
220
+ files.write_file(SETTINGS_FILE, content)
221
+
222
+
223
+def _get_default_settings() -> Settings:
224
+ return Settings(
225
+ chat_model_provider=ModelProvider.OPENAI.name,
226
+ chat_model_name="gpt-4o-mini",
227
+ chat_model_temperature=0,
228
+ chat_model_kwargs={},
229
+ util_model_provider=ModelProvider.OPENAI.name,
230
+ util_model_name="gpt-4o-mini",
231
+ util_model_temperature=0,
232
+ util_model_kwargs={},
233
+ embed_model_provider=ModelProvider.OPENAI.name,
234
+ embed_model_name="text-embedding-3-small",
235
+ embed_model_temperature=0,
236
+ embed_model_kwargs={},
237
+ )
238
+
239
+def _apply_settings():
240
+ global _settings
241
+ if _settings:
242
+ from agent import AgentContext
243
+ from initialize import initialize
244
+
245
+ for ctx in AgentContext._contexts.values():
246
+ ctx.config = initialize() # reinitialize context config with new settings
247
+ #apply config to agents
248
+ agent = ctx.agent0
249
+ while agent:
250
+ agent.config = ctx.config
251
+ agent = agent.get_data("subordinate")
252
+
253
+def _env_to_dict(data:str):
254
+ env_dict = {}
255
+ line_pattern = re.compile(r'\s*([^#][^=]*)\s*=\s*(.*)')
256
+ for line in data.splitlines():
257
+ match = line_pattern.match(line)
258
+ if match:
259
+ key, value = match.groups()
260
+ # Remove optional surrounding quotes (single or double)
261
+ value = value.strip().strip('"').strip("'")
262
+ env_dict[key.strip()] = value
263
+ return env_dict
264
+
265
+def _dict_to_env(data_dict):
266
+ lines = []
267
+ for key, value in data_dict.items():
268
+ if '\n' in value:
269
+ value = f"'{value}'"
270
+ elif ' ' in value or value == '' or any(c in value for c in '"\''):
271
+ value = f'"{value}"'
272
+ lines.append(f"{key}={value}")
273
+ return "\n".join(lines)
\ No newline at end of file
python/helpers/whisper_oai.py
new
+13
@@ -0,0 +1,13 @@
1
+# Import the necessary libraries
2
+import whisper
3
+import files
4
+
5
+# Load the base model from Whisper
6
+model = whisper.load_model("base")
7
+
8
+# Add your Audio File
9
+audio = files.get_abs_path("audio.ogg")
10
+
11
+# Transcribe the audio file
12
+result = model.transcribe(audio, fp16=False)
13
+print(result["text"])
\ No newline at end of file
run_ui.py
+46
-1
@@ -12,7 +12,7 @@ from python.helpers import files
12
from python.helpers.files import get_abs_path
13
from python.helpers.print_style import PrintStyle
14
from python.helpers.dotenv import load_dotenv
15
-from python.helpers import persist_chat
15
+from python.helpers import persist_chat, settings
16
17
18
# initialize the internal Flask server
@@ -348,6 +348,51 @@ async def poll():
348
# return jsonify(response)
349
350
351
+# get current settings
352
+@app.route("/getSettings", methods=["POST"])
353
+async def get_settings():
354
+ try:
355
+
356
+ # data sent to the server
357
+ input = request.get_json()
358
+
359
+ set = settings.convert_out(settings.get_settings())
360
+
361
+ response = {"ok": True, "settings": set}
362
+
363
+ except Exception as e:
364
+ response = {
365
+ "ok": False,
366
+ "message": str(e),
367
+ }
368
+ PrintStyle.error(str(e))
369
+
370
+ # respond with json
371
+ return jsonify(response)
372
+
373
+# set current settings
374
+@app.route("/setSettings", methods=["POST"])
375
+async def set_settings():
376
+ try:
377
+
378
+ # data sent to the server
379
+ input = request.get_json()
380
+
381
+ set = settings.convert_in(input)
382
+ set = settings.set_settings(set)
383
+
384
+ response = {"ok": True, "settings": set}
385
+
386
+ except Exception as e:
387
+ response = {
388
+ "ok": False,
389
+ "message": str(e),
390
+ }
391
+ PrintStyle.error(str(e))
392
+
393
+ # respond with json
394
+ return jsonify(response)
395
+
396
def run():
397
print("Initializing framework...")
398
webui/index.html
+114
@@ -7,6 +7,7 @@
7
<title>Agent Zero</title>
8
<link rel="stylesheet" href="index.css">
9
<link rel="stylesheet" href="toast.css">
10
+ <link rel="stylesheet" href="settings.css">
11
12
<script>
13
window.safeCall = function (name, ...args) {
@@ -15,6 +16,7 @@
16
</script>
17
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
18
<script type="module" src="index.js"></script>
19
+ <script type="text/javascript" src="settings.js"></script>
20
21
</head>
22
@@ -47,6 +49,8 @@
49
<button class="config-button" id="newChat" @click="newChat()">New Chat</button>
50
<button class="config-button" id="loadChats" @click="loadChats()">Load Chat</button>
51
<button class="config-button" id="loadChat" @click="saveChat()">Save Chat</button>
52
+ <button class="config-button" id="settings"
53
+ @click="settingsModalProxy.openModal()">Settings</button>
54
</div>
55
56
<div class="config-section" id="chats-section" x-data="{ contexts: [], selected: '' }"
@@ -153,6 +157,116 @@
157
</div>
158
</div>
159
</div>
160
+
161
+ <div id="settingsModal" x-data="settingsModalProxy">
162
+ <h1 x-text="settings.title"></h1>
163
+ <template x-teleport="body">
164
+ <div x-show="isOpen" class="modal-overlay" @click.self="handleCancel()"
165
+ x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0"
166
+ x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-200"
167
+ x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0">
168
+ <div class="modal-container">
169
+ <div class="modal-header">
170
+ <h2 x-text="settings.title"></h2>
171
+ <!-- Dynamically generated navigation -->
172
+ <nav>
173
+ <ul>
174
+ <!-- Loop over sections to generate links dynamically -->
175
+ <template x-for="(section, index) in settings.sections" :key="index">
176
+ <li>
177
+ <a :href="'#section' + (index + 1)" x-text="section.title"></a>
178
+ </li>
179
+ </template>
180
+ </ul>
181
+ </nav>
182
+ </div>
183
+
184
+ <div class="modal-content">
185
+ <template x-for="(section, sectionIndex) in settings.sections" :key="sectionIndex">
186
+ <div :id="'section' + (sectionIndex + 1)" class="section">
187
+ <div class="section-title" x-text="section.title"></div>
188
+ <div class="section-description" x-text="section.description"></div>
189
+
190
+ <template x-for="(field, fieldIndex) in section.fields" :key="fieldIndex">
191
+ <div :class="{'field': true, 'field-full': field.type === 'textarea'}">
192
+ <div class="field-label">
193
+ <div class="field-title" x-text="field.title"></div>
194
+ <div class="field-description" x-text="field.description"></div>
195
+ </div>
196
+
197
+ <div class="field-control">
198
+ <!-- Input field -->
199
+ <template x-if="field.type === 'input'">
200
+ <input type="text" :class="field.classes" :value="field.value"
201
+ :readonly="field.readonly === true"
202
+ @input="field.value = $event.target.value">
203
+ </template>
204
+
205
+ <!-- Textarea field -->
206
+ <template x-if="field.type === 'textarea'">
207
+ <textarea :class="field.classes" :value="field.value"
208
+ :readonly="field.readonly === true"
209
+ @input="field.value = $event.target.value"></textarea>
210
+ </template>
211
+
212
+ <!-- Switch field -->
213
+ <template x-if="field.type === 'switch'">
214
+ <label class="toggle">
215
+ <input type="checkbox" :checked="field.value"
216
+ :disabled="field.readonly === true"
217
+ @change="field.value = $event.target.checked">
218
+ <span class="toggler"></span>
219
+ </label>
220
+ </template>
221
+
222
+ <!-- Range field -->
223
+ <template x-if="field.type === 'range'">
224
+ <div class="field-control">
225
+ <input type="range" :min="field.min" :max="field.max"
226
+ :step="field.step" :value="field.value"
227
+ :disabled="field.readonly === true"
228
+ @input="field.value = $event.target.value"
229
+ :class="field.classes">
230
+ <span class="range-value" x-text="field.value"></span>
231
+ </div>
232
+ </template>
233
+
234
+ <!-- Button field -->
235
+ <template x-if="field.type === 'button'">
236
+ <button class="btn btn-field" :class="field.classes"
237
+ :disabled="field.readonly === true"
238
+ @click="handleFieldButton(field)" x-text="field.value"></button>
239
+ </template>
240
+
241
+ <!-- Select field -->
242
+ <template x-if="field.type === 'select'">
243
+ <select :class="field.classes" x-model="field.value"
244
+ :disabled="field.readonly === true">
245
+ <template x-for="option in field.options" :key="option.value">
246
+ <option :value="option.value" x-text="option.label"
247
+ :selected="option.value === field.value"></option>
248
+ </template>
249
+ </select>
250
+ </template>
251
+ </div>
252
+ </div>
253
+ </template>
254
+ </div>
255
+ </template>
256
+ </div>
257
+
258
+ <div class="modal-footer">
259
+ <template x-for="button in settings.buttons" :key="button.id">
260
+ <button :class="button.classes" @click="handleButton(button.id)"
261
+ x-text="button.title"></button>
262
+ </template>
263
+ </div>
264
+ </div>
265
+ </div>
266
+ </template>
267
+ </div>
268
+
269
+
270
</body>
271
272
</html>
\ No newline at end of file
webui/index.js
+1
-1
@@ -153,7 +153,7 @@ function adjustTextareaHeight() {
153
chatInput.style.height = (chatInput.scrollHeight) + 'px';
154
}
155
156
-async function sendJsonData(url, data) {
156
+window.sendJsonData = async function (url, data) {
157
const response = await fetch(url, {
158
method: 'POST',
159
headers: {
webui/settings.css
new
+231
@@ -0,0 +1,231 @@
1
+.modal-overlay {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ right: 0;
6
+ bottom: 0;
7
+ background: rgba(0, 0, 0, 0.5);
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+}
12
+
13
+.modal-container {
14
+ background: var(--color-panel);
15
+ color: var(--color-primary);
16
+ border-radius: 0.5rem;
17
+ width: 90%;
18
+ max-width: 800px;
19
+ max-height: 90vh;
20
+ display: flex;
21
+ flex-direction: column;
22
+}
23
+
24
+.modal-header {
25
+ padding: 1.5rem 2rem;
26
+ border-bottom: 1px solid #eee;
27
+}
28
+
29
+.modal-content {
30
+ padding: 2rem;
31
+ overflow-y: auto;
32
+ flex-grow: 1;
33
+}
34
+
35
+.modal-footer {
36
+ padding: 1.5rem 2rem;
37
+ border-top: 1px solid #eee;
38
+ display: flex;
39
+ justify-content: flex-end;
40
+ gap: 1rem;
41
+ background: var(--color-background);
42
+}
43
+
44
+.section {
45
+ margin-bottom: 2rem;
46
+ padding: 1rem;
47
+ border: 1px solid #eee;
48
+ border-radius: 0.5rem;
49
+}
50
+
51
+.section-title {
52
+ font-size: 1.25rem;
53
+ font-weight: bold;
54
+ margin-bottom: 0.5rem;
55
+}
56
+
57
+.section-description {
58
+ color: #666;
59
+ margin-bottom: 1rem;
60
+}
61
+
62
+.field {
63
+ margin-bottom: 1.5rem;
64
+ display: grid;
65
+ grid-template-columns: 200px 1fr;
66
+ gap: 1rem;
67
+ align-items: center;
68
+}
69
+
70
+.field.field-full {
71
+ grid-template-columns: 1fr;
72
+}
73
+
74
+.field-label {
75
+ display: flex;
76
+ flex-direction: column;
77
+}
78
+
79
+.field-title {
80
+ font-weight: bold;
81
+}
82
+
83
+.field-description {
84
+ color: #666;
85
+ font-size: 0.875rem;
86
+ margin-top: 0.25rem;
87
+}
88
+
89
+.field-control {
90
+ display: flex;
91
+ align-items: center;
92
+ gap: 0.5rem;
93
+}
94
+
95
+input[type="text"] {
96
+ width: 100%;
97
+ padding: 0.5rem;
98
+ border: 1px solid #ddd;
99
+ border-radius: 0.25rem;
100
+}
101
+
102
+textarea {
103
+ width: 100%;
104
+ min-height: 100px;
105
+ padding: 0.5rem;
106
+ border: 1px solid #ddd;
107
+ border-radius: 0.25rem;
108
+ margin-top: 1rem;
109
+ font-family: inherit;
110
+ resize: vertical;
111
+}
112
+
113
+.toggle {
114
+ position: relative;
115
+ display: inline-block;
116
+ width: 60px;
117
+ height: 34px;
118
+ margin: 0;
119
+}
120
+
121
+.toggle input {
122
+ opacity: 0;
123
+ width: 0;
124
+ height: 0;
125
+}
126
+
127
+.toggler {
128
+ position: absolute;
129
+ cursor: pointer;
130
+ top: 0;
131
+ left: 0;
132
+ right: 0;
133
+ bottom: 0;
134
+ background-color: #ccc;
135
+ transition: .4s;
136
+ border-radius: 34px;
137
+}
138
+
139
+.toggler:before {
140
+ position: absolute;
141
+ content: "";
142
+ height: 26px;
143
+ width: 26px;
144
+ left: 4px;
145
+ bottom: 4px;
146
+ background-color: white;
147
+ transition: .4s;
148
+ border-radius: 50%;
149
+}
150
+
151
+input:checked+.toggler {
152
+ background-color: #2196F3;
153
+}
154
+
155
+input:checked+.toggler:before {
156
+ transform: translateX(26px);
157
+}
158
+
159
+input[type="range"] {
160
+ width: 100%;
161
+}
162
+
163
+.range-value {
164
+ min-width: 3em;
165
+ text-align: right;
166
+}
167
+
168
+.btn {
169
+ padding: 0.5rem 1rem;
170
+ border-radius: 0.25rem;
171
+ cursor: pointer;
172
+ border: none;
173
+ font-size: 0.875rem;
174
+}
175
+
176
+.btn-ok {
177
+ background: #2196F3;
178
+ color: white;
179
+}
180
+
181
+.btn-cancel {
182
+ background: #ddd;
183
+ color: #333;
184
+}
185
+
186
+.btn-field {
187
+ background: #2196F3;
188
+ color: white;
189
+ width: fit-content;
190
+}
191
+
192
+.btn-field:disabled {
193
+ background: #ccc;
194
+ cursor: not-allowed;
195
+}
196
+
197
+select {
198
+ width: 100%;
199
+ padding: 0.5rem;
200
+ border: 1px solid #ddd;
201
+ border-radius: 0.25rem;
202
+ background-color: white;
203
+ font-size: inherit;
204
+ cursor: pointer;
205
+}
206
+
207
+select:disabled {
208
+ background-color: #f5f5f5;
209
+ cursor: not-allowed;
210
+}
211
+
212
+/* Style for navigation links */
213
+nav ul {
214
+ list-style-type: none;
215
+ padding: 0;
216
+}
217
+
218
+nav ul li {
219
+ display: inline;
220
+ margin-right: 1rem;
221
+}
222
+
223
+nav ul li a {
224
+ text-decoration: none;
225
+ color: #2196F3;
226
+ font-weight: bold;
227
+}
228
+
229
+nav ul li a:hover {
230
+ text-decoration: underline;
231
+}
\ No newline at end of file
webui/settings.js
new
+87
@@ -0,0 +1,87 @@
1
+const settingsModalProxy = {
2
+ isOpen: false,
3
+ settings: {},
4
+ resolvePromise: null,
5
+
6
+
7
+ async openModal() {
8
+
9
+ const modalEl = document.getElementById('settingsModal');
10
+ const modalAD = Alpine.$data(modalEl);
11
+
12
+ //get settings from backend
13
+ const set = await sendJsonData("/getSettings", null);
14
+
15
+ const settings = {
16
+ "title": "Settings page",
17
+ "buttons": [
18
+ {
19
+ "id": "save",
20
+ "title": "Save",
21
+ "classes": "btn btn-ok"
22
+ },
23
+ {
24
+ "id": "cancel",
25
+ "title": "Cancel",
26
+ "type": "secondary",
27
+ "classes": "btn btn-cancel"
28
+ }
29
+ ],
30
+ "sections": set.settings.sections
31
+ }
32
+
33
+ modalAD.isOpen = true; // Update directly
34
+ modalAD.settings = settings; // Update directly
35
+
36
+ return new Promise(resolve => {
37
+ this.resolvePromise = resolve;
38
+ });
39
+ },
40
+
41
+ async handleButton(buttonId) {
42
+ if (buttonId === 'save') {
43
+
44
+ const modalEl = document.getElementById('settingsModal');
45
+ const modalAD = Alpine.$data(modalEl);
46
+ resp = await window.sendJsonData("/setSettings", modalAD.settings);
47
+
48
+ this.resolvePromise({
49
+ status: 'saved',
50
+ data: resp.settings
51
+ });
52
+ } else if (buttonId === 'cancel') {
53
+ this.handleCancel();
54
+ }
55
+ this.isOpen = false;
56
+ },
57
+
58
+ async handleCancel() {
59
+ this.resolvePromise({
60
+ status: 'cancelled',
61
+ data: null
62
+ });
63
+ this.isOpen = false;
64
+ },
65
+
66
+ handleFieldButton(field) {
67
+ console.log(`Button clicked: ${field.action}`);
68
+ }
69
+};
70
+
71
+
72
+// function initSettingsModal() {
73
+
74
+// window.openSettings = function () {
75
+// proxy.openModal().then(result => {
76
+// console.log(result); // This will log the result when the modal is closed
77
+// });
78
+// }
79
+
80
+// return proxy
81
+// }
82
+
83
+
84
+// document.addEventListener('alpine:init', () => {
85
+// Alpine.store('settingsModal', initSettingsModal());
86
+// });
87
+