asktable-advisor 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asktable_advisor/__init__.py +18 -0
- asktable_advisor/__main__.py +156 -0
- asktable_advisor/__version__.py +4 -0
- asktable_advisor/agent/__init__.py +6 -0
- asktable_advisor/agent/advisor.py +337 -0
- asktable_advisor/agent/llm_client.py +195 -0
- asktable_advisor/agent/prompts.py +135 -0
- asktable_advisor/agent/tools.py +324 -0
- asktable_advisor/asktable/__init__.py +0 -0
- asktable_advisor/asktable/client.py +271 -0
- asktable_advisor/asktable/inspector.py +210 -0
- asktable_advisor/asktable/resources/__init__.py +0 -0
- asktable_advisor/config.py +79 -0
- asktable_advisor/conversation/__init__.py +0 -0
- asktable_advisor/database/__init__.py +0 -0
- asktable_advisor/database/data_generator.py +143 -0
- asktable_advisor/database/manager.py +228 -0
- asktable_advisor/database/schema_generator.py +148 -0
- asktable_advisor/knowledge/__init__.py +0 -0
- asktable_advisor/utils/__init__.py +0 -0
- asktable_advisor-1.0.1.dist-info/METADATA +265 -0
- asktable_advisor-1.0.1.dist-info/RECORD +26 -0
- asktable_advisor-1.0.1.dist-info/WHEEL +5 -0
- asktable_advisor-1.0.1.dist-info/entry_points.txt +2 -0
- asktable_advisor-1.0.1.dist-info/licenses/LICENSE +201 -0
- asktable_advisor-1.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""AskTable API client for Advisor Agent."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from asktable import Asktable
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AskTableClient:
|
|
12
|
+
"""
|
|
13
|
+
AskTable API client wrapper.
|
|
14
|
+
|
|
15
|
+
Provides a clean interface to interact with AskTable resources.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, api_key: str, base_url: str):
|
|
19
|
+
"""
|
|
20
|
+
Initialize AskTable client.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
api_key: AskTable API key
|
|
24
|
+
base_url: AskTable API base URL
|
|
25
|
+
"""
|
|
26
|
+
self.api_key = api_key
|
|
27
|
+
self.base_url = base_url
|
|
28
|
+
self._client: Optional[Asktable] = None
|
|
29
|
+
|
|
30
|
+
logger.info(f"AskTable client initialized: {base_url}")
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def client(self) -> Asktable:
|
|
34
|
+
"""Get or create AskTable SDK client."""
|
|
35
|
+
if self._client is None:
|
|
36
|
+
self._client = Asktable(
|
|
37
|
+
base_url=self.base_url,
|
|
38
|
+
api_key=self.api_key,
|
|
39
|
+
)
|
|
40
|
+
return self._client
|
|
41
|
+
|
|
42
|
+
def test_connection(self) -> bool:
|
|
43
|
+
"""
|
|
44
|
+
Test AskTable API connection.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
True if connection successful, False otherwise
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
self.client.datasources.list()
|
|
51
|
+
logger.info("AskTable API connection successful")
|
|
52
|
+
return True
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.error(f"AskTable API connection failed: {e}")
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
# Datasource operations
|
|
58
|
+
def list_datasources(self) -> List[Dict[str, Any]]:
|
|
59
|
+
"""List all datasources in the project."""
|
|
60
|
+
try:
|
|
61
|
+
result = self.client.datasources.list()
|
|
62
|
+
datasources = result.items if hasattr(result, 'items') else result
|
|
63
|
+
|
|
64
|
+
ds_list = []
|
|
65
|
+
for ds in datasources:
|
|
66
|
+
if hasattr(ds, 'model_dump'):
|
|
67
|
+
ds_list.append(ds.model_dump())
|
|
68
|
+
elif hasattr(ds, 'dict'):
|
|
69
|
+
ds_list.append(ds.dict())
|
|
70
|
+
else:
|
|
71
|
+
ds_list.append(ds)
|
|
72
|
+
return ds_list
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.error(f"Failed to list datasources: {e}")
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
def get_datasource(self, datasource_id: str) -> Optional[Dict[str, Any]]:
|
|
78
|
+
"""Get datasource by ID."""
|
|
79
|
+
try:
|
|
80
|
+
datasource = self.client.datasources.retrieve(datasource_id)
|
|
81
|
+
if hasattr(datasource, 'model_dump'):
|
|
82
|
+
return datasource.model_dump()
|
|
83
|
+
elif hasattr(datasource, 'dict'):
|
|
84
|
+
return datasource.dict()
|
|
85
|
+
return datasource
|
|
86
|
+
except Exception as e:
|
|
87
|
+
logger.warning(f"Datasource not found: {e}")
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
def create_datasource(
|
|
91
|
+
self,
|
|
92
|
+
name: str,
|
|
93
|
+
database_name: str,
|
|
94
|
+
description: Optional[str] = None,
|
|
95
|
+
host: Optional[str] = None,
|
|
96
|
+
port: Optional[int] = None,
|
|
97
|
+
username: Optional[str] = None,
|
|
98
|
+
password: Optional[str] = None,
|
|
99
|
+
) -> Dict[str, Any]:
|
|
100
|
+
"""Create a new datasource."""
|
|
101
|
+
try:
|
|
102
|
+
# Use provided connection info or fall back to env defaults
|
|
103
|
+
from ..config import get_settings
|
|
104
|
+
settings = get_settings()
|
|
105
|
+
|
|
106
|
+
datasource = self.client.datasources.create(
|
|
107
|
+
name=name,
|
|
108
|
+
engine="mysql",
|
|
109
|
+
access_config={
|
|
110
|
+
"host": host or settings.mysql_host,
|
|
111
|
+
"port": port or settings.mysql_port,
|
|
112
|
+
"db": database_name,
|
|
113
|
+
"user": username or settings.mysql_user,
|
|
114
|
+
"password": password or settings.mysql_password,
|
|
115
|
+
},
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
logger.info(f"Created datasource: {name}")
|
|
119
|
+
|
|
120
|
+
if hasattr(datasource, 'model_dump'):
|
|
121
|
+
return datasource.model_dump()
|
|
122
|
+
elif hasattr(datasource, 'dict'):
|
|
123
|
+
return datasource.dict()
|
|
124
|
+
return datasource
|
|
125
|
+
except Exception as e:
|
|
126
|
+
logger.error(f"Failed to create datasource: {e}")
|
|
127
|
+
raise
|
|
128
|
+
|
|
129
|
+
def delete_datasource(self, datasource_id: str) -> bool:
|
|
130
|
+
"""Delete a datasource."""
|
|
131
|
+
try:
|
|
132
|
+
self.client.datasources.delete(datasource_id)
|
|
133
|
+
logger.info(f"Deleted datasource: {datasource_id}")
|
|
134
|
+
return True
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.error(f"Failed to delete datasource: {e}")
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
# Bot operations
|
|
140
|
+
def list_bots(self) -> List[Dict[str, Any]]:
|
|
141
|
+
"""List all bots in the project."""
|
|
142
|
+
try:
|
|
143
|
+
result = self.client.bots.list()
|
|
144
|
+
bots = result.items if hasattr(result, 'items') else result
|
|
145
|
+
|
|
146
|
+
bot_list = []
|
|
147
|
+
for bot in bots:
|
|
148
|
+
if hasattr(bot, 'model_dump'):
|
|
149
|
+
bot_list.append(bot.model_dump())
|
|
150
|
+
elif hasattr(bot, 'dict'):
|
|
151
|
+
bot_list.append(bot.dict())
|
|
152
|
+
else:
|
|
153
|
+
bot_list.append(bot)
|
|
154
|
+
return bot_list
|
|
155
|
+
except Exception as e:
|
|
156
|
+
logger.error(f"Failed to list bots: {e}")
|
|
157
|
+
return []
|
|
158
|
+
|
|
159
|
+
def get_bot(self, bot_id: str) -> Optional[Dict[str, Any]]:
|
|
160
|
+
"""Get bot by ID."""
|
|
161
|
+
try:
|
|
162
|
+
bot = self.client.bots.retrieve(bot_id)
|
|
163
|
+
if hasattr(bot, 'model_dump'):
|
|
164
|
+
return bot.model_dump()
|
|
165
|
+
elif hasattr(bot, 'dict'):
|
|
166
|
+
return bot.dict()
|
|
167
|
+
return bot
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.warning(f"Bot not found: {e}")
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
def create_bot(
|
|
173
|
+
self,
|
|
174
|
+
name: str,
|
|
175
|
+
datasource_id: str,
|
|
176
|
+
description: Optional[str] = None,
|
|
177
|
+
instructions: Optional[str] = None,
|
|
178
|
+
sample_questions: Optional[List[str]] = None,
|
|
179
|
+
) -> Dict[str, Any]:
|
|
180
|
+
"""Create a new bot."""
|
|
181
|
+
try:
|
|
182
|
+
bot = self.client.bots.create(
|
|
183
|
+
name=name,
|
|
184
|
+
datasource_ids=[datasource_id],
|
|
185
|
+
welcome_message=description,
|
|
186
|
+
magic_input=instructions,
|
|
187
|
+
sample_questions=sample_questions or [],
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
logger.info(f"Created bot: {name}")
|
|
191
|
+
|
|
192
|
+
if hasattr(bot, 'model_dump'):
|
|
193
|
+
return bot.model_dump()
|
|
194
|
+
elif hasattr(bot, 'dict'):
|
|
195
|
+
return bot.dict()
|
|
196
|
+
return bot
|
|
197
|
+
except Exception as e:
|
|
198
|
+
logger.error(f"Failed to create bot: {e}")
|
|
199
|
+
raise
|
|
200
|
+
|
|
201
|
+
def update_bot(
|
|
202
|
+
self,
|
|
203
|
+
bot_id: str,
|
|
204
|
+
name: Optional[str] = None,
|
|
205
|
+
instructions: Optional[str] = None,
|
|
206
|
+
sample_questions: Optional[List[str]] = None,
|
|
207
|
+
) -> Dict[str, Any]:
|
|
208
|
+
"""Update an existing bot."""
|
|
209
|
+
try:
|
|
210
|
+
update_data = {}
|
|
211
|
+
if name:
|
|
212
|
+
update_data["name"] = name
|
|
213
|
+
if instructions:
|
|
214
|
+
update_data["magic_input"] = instructions
|
|
215
|
+
if sample_questions:
|
|
216
|
+
update_data["sample_questions"] = sample_questions
|
|
217
|
+
|
|
218
|
+
bot = self.client.bots.update(bot_id, **update_data)
|
|
219
|
+
logger.info(f"Updated bot: {bot_id}")
|
|
220
|
+
|
|
221
|
+
if hasattr(bot, 'model_dump'):
|
|
222
|
+
return bot.model_dump()
|
|
223
|
+
elif hasattr(bot, 'dict'):
|
|
224
|
+
return bot.dict()
|
|
225
|
+
return bot
|
|
226
|
+
except Exception as e:
|
|
227
|
+
logger.error(f"Failed to update bot: {e}")
|
|
228
|
+
raise
|
|
229
|
+
|
|
230
|
+
# Chat operations
|
|
231
|
+
def list_chats(self) -> List[Dict[str, Any]]:
|
|
232
|
+
"""List all chats in the project."""
|
|
233
|
+
try:
|
|
234
|
+
result = self.client.chats.list()
|
|
235
|
+
chats = result.items if hasattr(result, 'items') else result
|
|
236
|
+
|
|
237
|
+
chat_list = []
|
|
238
|
+
for chat in chats:
|
|
239
|
+
if hasattr(chat, 'model_dump'):
|
|
240
|
+
chat_list.append(chat.model_dump())
|
|
241
|
+
elif hasattr(chat, 'dict'):
|
|
242
|
+
chat_list.append(chat.dict())
|
|
243
|
+
else:
|
|
244
|
+
chat_list.append(chat)
|
|
245
|
+
return chat_list
|
|
246
|
+
except Exception as e:
|
|
247
|
+
logger.error(f"Failed to list chats: {e}")
|
|
248
|
+
return []
|
|
249
|
+
|
|
250
|
+
def create_chat(
|
|
251
|
+
self,
|
|
252
|
+
bot_id: str,
|
|
253
|
+
name: str,
|
|
254
|
+
) -> Dict[str, Any]:
|
|
255
|
+
"""Create a new chat session."""
|
|
256
|
+
try:
|
|
257
|
+
chat = self.client.chats.create(
|
|
258
|
+
bot_id=bot_id,
|
|
259
|
+
name=name,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
logger.info(f"Created chat: {name}")
|
|
263
|
+
|
|
264
|
+
if hasattr(chat, 'model_dump'):
|
|
265
|
+
return chat.model_dump()
|
|
266
|
+
elif hasattr(chat, 'dict'):
|
|
267
|
+
return chat.dict()
|
|
268
|
+
return chat
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logger.error(f"Failed to create chat: {e}")
|
|
271
|
+
raise
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""AskTable project inspector for analyzing existing resources."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict, List
|
|
5
|
+
|
|
6
|
+
from .client import AskTableClient
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AskTableInspector:
|
|
12
|
+
"""
|
|
13
|
+
Inspector for analyzing AskTable projects.
|
|
14
|
+
|
|
15
|
+
Reads and organizes information about existing datasources, bots, chats, etc.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client: AskTableClient):
|
|
19
|
+
"""
|
|
20
|
+
Initialize inspector.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
client: AskTable client instance
|
|
24
|
+
"""
|
|
25
|
+
self.client = client
|
|
26
|
+
|
|
27
|
+
def inspect_project(self, detailed: bool = False) -> Dict[str, Any]:
|
|
28
|
+
"""
|
|
29
|
+
Inspect the entire AskTable project.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
detailed: If True, include full configuration for each resource
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Dict containing organized project information
|
|
36
|
+
"""
|
|
37
|
+
logger.info("Inspecting AskTable project...")
|
|
38
|
+
|
|
39
|
+
project_info = {
|
|
40
|
+
"datasources": self._inspect_datasources(detailed),
|
|
41
|
+
"bots": self._inspect_bots(detailed),
|
|
42
|
+
"chats": self._inspect_chats(detailed),
|
|
43
|
+
"summary": {},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Add summary statistics
|
|
47
|
+
project_info["summary"] = {
|
|
48
|
+
"total_datasources": len(project_info["datasources"]),
|
|
49
|
+
"total_bots": len(project_info["bots"]),
|
|
50
|
+
"total_chats": len(project_info["chats"]),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
logger.info(
|
|
54
|
+
f"Project inspection complete: "
|
|
55
|
+
f"{project_info['summary']['total_datasources']} datasources, "
|
|
56
|
+
f"{project_info['summary']['total_bots']} bots, "
|
|
57
|
+
f"{project_info['summary']['total_chats']} chats"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return project_info
|
|
61
|
+
|
|
62
|
+
def _inspect_datasources(self, detailed: bool) -> List[Dict[str, Any]]:
|
|
63
|
+
"""Inspect all datasources."""
|
|
64
|
+
datasources = self.client.list_datasources()
|
|
65
|
+
|
|
66
|
+
if not detailed:
|
|
67
|
+
# Return simplified view
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
"id": ds.get("id"),
|
|
71
|
+
"name": ds.get("name"),
|
|
72
|
+
"engine": ds.get("engine"),
|
|
73
|
+
"database": ds.get("access_config", {}).get("db"),
|
|
74
|
+
}
|
|
75
|
+
for ds in datasources
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
return datasources
|
|
79
|
+
|
|
80
|
+
def _inspect_bots(self, detailed: bool) -> List[Dict[str, Any]]:
|
|
81
|
+
"""Inspect all bots."""
|
|
82
|
+
bots = self.client.list_bots()
|
|
83
|
+
|
|
84
|
+
if not detailed:
|
|
85
|
+
# Return simplified view
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
"id": bot.get("id"),
|
|
89
|
+
"name": bot.get("name"),
|
|
90
|
+
"datasource_ids": bot.get("datasource_ids", []),
|
|
91
|
+
"sample_question_count": len(bot.get("sample_questions", [])),
|
|
92
|
+
}
|
|
93
|
+
for bot in bots
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
return bots
|
|
97
|
+
|
|
98
|
+
def _inspect_chats(self, detailed: bool) -> List[Dict[str, Any]]:
|
|
99
|
+
"""Inspect all chats."""
|
|
100
|
+
chats = self.client.list_chats()
|
|
101
|
+
|
|
102
|
+
if not detailed:
|
|
103
|
+
# Return simplified view
|
|
104
|
+
return [
|
|
105
|
+
{
|
|
106
|
+
"id": chat.get("id"),
|
|
107
|
+
"name": chat.get("name"),
|
|
108
|
+
"bot_id": chat.get("bot_id"),
|
|
109
|
+
}
|
|
110
|
+
for chat in chats
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
return chats
|
|
114
|
+
|
|
115
|
+
def identify_scenarios(self) -> List[Dict[str, Any]]:
|
|
116
|
+
"""
|
|
117
|
+
Identify distinct scenarios in the project.
|
|
118
|
+
|
|
119
|
+
A scenario is typically a datasource + associated bots + chats.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
List of identified scenarios
|
|
123
|
+
"""
|
|
124
|
+
datasources = self.client.list_datasources()
|
|
125
|
+
bots = self.client.list_bots()
|
|
126
|
+
chats = self.client.list_chats()
|
|
127
|
+
|
|
128
|
+
scenarios = []
|
|
129
|
+
|
|
130
|
+
for ds in datasources:
|
|
131
|
+
ds_id = ds.get("id")
|
|
132
|
+
|
|
133
|
+
# Find bots using this datasource
|
|
134
|
+
related_bots = [
|
|
135
|
+
bot for bot in bots
|
|
136
|
+
if ds_id in bot.get("datasource_ids", [])
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
# Find chats for these bots
|
|
140
|
+
related_chats = []
|
|
141
|
+
for bot in related_bots:
|
|
142
|
+
bot_id = bot.get("id")
|
|
143
|
+
bot_chats = [
|
|
144
|
+
chat for chat in chats
|
|
145
|
+
if chat.get("bot_id") == bot_id
|
|
146
|
+
]
|
|
147
|
+
related_chats.extend(bot_chats)
|
|
148
|
+
|
|
149
|
+
scenario = {
|
|
150
|
+
"datasource": {
|
|
151
|
+
"id": ds.get("id"),
|
|
152
|
+
"name": ds.get("name"),
|
|
153
|
+
"engine": ds.get("engine"),
|
|
154
|
+
},
|
|
155
|
+
"bots": [
|
|
156
|
+
{
|
|
157
|
+
"id": bot.get("id"),
|
|
158
|
+
"name": bot.get("name"),
|
|
159
|
+
"sample_question_count": len(bot.get("sample_questions", [])),
|
|
160
|
+
}
|
|
161
|
+
for bot in related_bots
|
|
162
|
+
],
|
|
163
|
+
"chats": [
|
|
164
|
+
{
|
|
165
|
+
"id": chat.get("id"),
|
|
166
|
+
"name": chat.get("name"),
|
|
167
|
+
}
|
|
168
|
+
for chat in related_chats
|
|
169
|
+
],
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
scenarios.append(scenario)
|
|
173
|
+
|
|
174
|
+
logger.info(f"Identified {len(scenarios)} scenarios")
|
|
175
|
+
return scenarios
|
|
176
|
+
|
|
177
|
+
def get_optimization_suggestions(self) -> List[str]:
|
|
178
|
+
"""
|
|
179
|
+
Analyze project and provide optimization suggestions.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
List of suggestion strings
|
|
183
|
+
"""
|
|
184
|
+
suggestions = []
|
|
185
|
+
|
|
186
|
+
datasources = self.client.list_datasources()
|
|
187
|
+
bots = self.client.list_bots()
|
|
188
|
+
|
|
189
|
+
# Check for datasources without bots
|
|
190
|
+
for ds in datasources:
|
|
191
|
+
ds_id = ds.get("id")
|
|
192
|
+
has_bots = any(
|
|
193
|
+
ds_id in bot.get("datasource_ids", [])
|
|
194
|
+
for bot in bots
|
|
195
|
+
)
|
|
196
|
+
if not has_bots:
|
|
197
|
+
suggestions.append(
|
|
198
|
+
f"数据源 '{ds.get('name')}' 没有关联的 Bot,建议创建一个 Bot 来使用这个数据源"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Check for bots with few sample questions
|
|
202
|
+
for bot in bots:
|
|
203
|
+
question_count = len(bot.get("sample_questions", []))
|
|
204
|
+
if question_count < 10:
|
|
205
|
+
suggestions.append(
|
|
206
|
+
f"Bot '{bot.get('name')}' 只有 {question_count} 个示例问题,"
|
|
207
|
+
f"建议添加更多(推荐 15-25 个)"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return suggestions
|
|
File without changes
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Configuration management for AskTable Advisor AI Agent."""
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AdvisorSettings(BaseSettings):
|
|
8
|
+
"""Application settings loaded from environment variables."""
|
|
9
|
+
|
|
10
|
+
model_config = SettingsConfigDict(
|
|
11
|
+
env_file=".env",
|
|
12
|
+
env_file_encoding="utf-8",
|
|
13
|
+
case_sensitive=False,
|
|
14
|
+
extra="ignore",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# AskTable API Configuration
|
|
18
|
+
asktable_api_key: str = Field(..., description="AskTable API key")
|
|
19
|
+
asktable_api_base: str = Field(
|
|
20
|
+
default="https://api.asktable.com",
|
|
21
|
+
description="AskTable API base URL",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# LLM API Configuration (Aliyun Bailian - qwen3-max)
|
|
25
|
+
anthropic_base_url: str = Field(
|
|
26
|
+
default="https://dashscope.aliyuncs.com/apps/anthropic",
|
|
27
|
+
description="Anthropic-compatible API base URL (Aliyun Bailian)",
|
|
28
|
+
)
|
|
29
|
+
anthropic_api_key: str = Field(
|
|
30
|
+
...,
|
|
31
|
+
description="DashScope API key for Aliyun Bailian",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Agent Configuration
|
|
35
|
+
agent_model: str = Field(
|
|
36
|
+
default="qwen3-max",
|
|
37
|
+
description="LLM model to use for the advisor agent",
|
|
38
|
+
)
|
|
39
|
+
agent_temperature: float = Field(
|
|
40
|
+
default=0.7,
|
|
41
|
+
description="Temperature for LLM responses (0.0-1.0)",
|
|
42
|
+
)
|
|
43
|
+
agent_max_tokens: int = Field(
|
|
44
|
+
default=4096,
|
|
45
|
+
description="Maximum tokens for LLM responses",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# MySQL Database Configuration
|
|
49
|
+
mysql_host: str = Field(..., description="MySQL host")
|
|
50
|
+
mysql_port: int = Field(default=3306, description="MySQL port")
|
|
51
|
+
mysql_user: str = Field(..., description="MySQL username")
|
|
52
|
+
mysql_password: str = Field(..., description="MySQL password")
|
|
53
|
+
mysql_database: str = Field(
|
|
54
|
+
default="asktable_advisor",
|
|
55
|
+
description="MySQL database name",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Optional: Database connection pool settings
|
|
59
|
+
mysql_pool_size: int = Field(
|
|
60
|
+
default=5,
|
|
61
|
+
description="Database connection pool size",
|
|
62
|
+
)
|
|
63
|
+
mysql_max_overflow: int = Field(
|
|
64
|
+
default=10,
|
|
65
|
+
description="Maximum overflow connections",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def mysql_url(self) -> str:
|
|
70
|
+
"""Generate SQLAlchemy database URL."""
|
|
71
|
+
return (
|
|
72
|
+
f"mysql+pymysql://{self.mysql_user}:{self.mysql_password}"
|
|
73
|
+
f"@{self.mysql_host}:{self.mysql_port}/{self.mysql_database}"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_settings() -> AdvisorSettings:
|
|
78
|
+
"""Load and return application settings."""
|
|
79
|
+
return AdvisorSettings()
|
|
File without changes
|
|
File without changes
|