skill-self-evolution 0.2.0__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 (29) hide show
  1. skill_self_evolution-0.2.0/PKG-INFO +12 -0
  2. skill_self_evolution-0.2.0/README.md +99 -0
  3. skill_self_evolution-0.2.0/pyproject.toml +25 -0
  4. skill_self_evolution-0.2.0/setup.cfg +4 -0
  5. skill_self_evolution-0.2.0/src/skill_self_evolution/__init__.py +27 -0
  6. skill_self_evolution-0.2.0/src/skill_self_evolution/ai_assisted_executor.py +244 -0
  7. skill_self_evolution-0.2.0/src/skill_self_evolution/config.py +87 -0
  8. skill_self_evolution-0.2.0/src/skill_self_evolution/config_loader.py +241 -0
  9. skill_self_evolution-0.2.0/src/skill_self_evolution/context.py +22 -0
  10. skill_self_evolution-0.2.0/src/skill_self_evolution/deepseek.py +168 -0
  11. skill_self_evolution-0.2.0/src/skill_self_evolution/evolver.py +386 -0
  12. skill_self_evolution-0.2.0/src/skill_self_evolution/executor.py +471 -0
  13. skill_self_evolution-0.2.0/src/skill_self_evolution/fallback.py +129 -0
  14. skill_self_evolution-0.2.0/src/skill_self_evolution/loader.py +201 -0
  15. skill_self_evolution-0.2.0/src/skill_self_evolution/logger.py +84 -0
  16. skill_self_evolution-0.2.0/src/skill_self_evolution/models.py +147 -0
  17. skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/PKG-INFO +12 -0
  18. skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/SOURCES.txt +27 -0
  19. skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/dependency_links.txt +1 -0
  20. skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/requires.txt +8 -0
  21. skill_self_evolution-0.2.0/src/skill_self_evolution.egg-info/top_level.txt +1 -0
  22. skill_self_evolution-0.2.0/tests/test_ai_assisted_executor.py +121 -0
  23. skill_self_evolution-0.2.0/tests/test_context.py +37 -0
  24. skill_self_evolution-0.2.0/tests/test_deepseek.py +48 -0
  25. skill_self_evolution-0.2.0/tests/test_executor.py +160 -0
  26. skill_self_evolution-0.2.0/tests/test_fallback.py +71 -0
  27. skill_self_evolution-0.2.0/tests/test_loader.py +128 -0
  28. skill_self_evolution-0.2.0/tests/test_logger.py +64 -0
  29. skill_self_evolution-0.2.0/tests/test_models.py +76 -0
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: skill_self_evolution
3
+ Version: 0.2.0
4
+ Summary: Skill 自进化框架:Pydantic 全链路校验 + 规则执行 + AI 常识判断 + 离线进化的可插拔 Skill 执行引擎
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: pydantic>=2.0
7
+ Requires-Dist: pyyaml>=6.0
8
+ Requires-Dist: httpx>=0.25
9
+ Requires-Dist: pymysql>=1.1
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=8.0; extra == "dev"
12
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
@@ -0,0 +1,99 @@
1
+ # skill_self_evolution v0.2.0
2
+
3
+ Skill 自进化框架:**Pydantic 全链路校验** + 规则执行 + AI 常识判断 + 离线进化的可插拔 Skill 执行引擎。
4
+
5
+ ## v0.2.0 更新
6
+
7
+ - **Pydantic 全链路覆盖**:12 个源文件,8 个完整 Pydantic 校验(4 个有理据豁免)
8
+ - 新增 11 个 Pydantic 模型,覆盖 AI 中间结果、DeepSeek 响应、JSONL 日志、降级配置、进化提案、Skill 子结构
9
+ - `AiValidationResult` / `AiReselectionResult` 用 `Literal["合理","不合理"]` 替代裸 `dict.get("result")`
10
+ - `DeepSeekChatResponse` 替代 dataclass,字段缺失自动默认值
11
+ - `ConfigVersionManager` 兼容 `dict` 和 `DbConfig` 双路径
12
+
13
+ ## 架构
14
+
15
+ ```
16
+ 输入 (SkillInput) 规则执行 AI 校验 AI 重选 日志 & 进化
17
+ input_data → execute() → _ai_validate() → _ai_reselect() → JSONL + Evolver
18
+ │ │ │ │ │
19
+ └─ Pydantic ✅ └─ SkillOutput ✅ └─ AiValidation └─ AiReselection └─ LogEntry/Proposal
20
+ Result ✅ Result ✅ ✅
21
+ ```
22
+
23
+ ## Pydantic 模型清单
24
+
25
+ ### 核心框架
26
+
27
+ | 模型 | 作用 | 校验内容 |
28
+ |---|---|---|
29
+ | `SkillInput[T]` | 入口 | `trace_id` + `input_data`(泛型) |
30
+ | `SkillOutput` | 出口 | `source` + `result` + `ai_validated` + `ai_reselected` |
31
+
32
+ ### AI 中间结果(v0.2.0 新增)
33
+
34
+ | 模型 | 作用 | 校验约束 |
35
+ |---|---|---|
36
+ | `AiValidationResult` | AI 验证输出 | `Literal["合理","不合理"]` + reason ≤500字 |
37
+ | `AiReselectionResult` | AI 重选输出 | `result: str` + reason ≤500字 |
38
+
39
+ ### 数据层(v0.2.0 新增)
40
+
41
+ | 模型 | 作用 |
42
+ |---|---|
43
+ | `DeepSeekChatResponse` | HTTP 响应结构校验 |
44
+ | `LogEntry` | JSONL 日志条目(11 字段全默认值容错) |
45
+ | `FallbackConfigModel` | 降级参数(gt/ge/le 约束) |
46
+ | `EvolveProposalModel` | 进化提案序列化 |
47
+ | `NicknameSkillResult` | nickname-selector 子结构示例 |
48
+ | `DeepSeekEnvConfig` | API 连接配置 |
49
+ | `DbConfig` | 数据库连接配置(port ge=1 le=65535) |
50
+
51
+ ## 快速开始
52
+
53
+ ```python
54
+ from skill_self_evolution import SkillExecutor
55
+
56
+ executor = SkillExecutor(
57
+ deepseek_api_key="sk-xxx", # 或设置 DEEPSEEK_API_KEY
58
+ skill_base_dir="/path/to/skills",
59
+ )
60
+ result = await executor.run("nickname-selector", {
61
+ "screenshot_id": "scr_004",
62
+ "metadata_path": "/path/to/metadata.json",
63
+ "debug_session_derived_path": "/path/to/debug_session_derived.json",
64
+ })
65
+ ```
66
+
67
+ ## 安装
68
+
69
+ ```bash
70
+ pip install skill_self_evolution
71
+ ```
72
+
73
+ ## 开发
74
+
75
+ ```bash
76
+ # 本地安装(开发模式)
77
+ pip install -e .
78
+
79
+ # 运行测试
80
+ pytest
81
+ ```
82
+
83
+ ## 环境变量
84
+
85
+ | 变量 | local 默认 | test/prod 默认 |
86
+ |---|---|---|
87
+ | `DEEPSEEK_API_KEY` | DeepSeek 官网 Key | — |
88
+ | `DEEPSEEK_API_BASE` | `https://api.deepseek.com/v1` | — |
89
+ | `WX_MATCH_DEEPSEEK_API_KEY` | — | 华为云 MaaS Key |
90
+ | `WX_MATCH_DEEPSEEK_API_BASE` | — | `https://api.modelarts-maas.com/v2` |
91
+ | `APP_ENV` | `local` | `test` / `prod` |
92
+ | `DB_HOST` | `127.0.0.1` | 环境注入 |
93
+ | `DB_USER` / `DB_PASSWORD` / `DB_NAME` | `root` / (空) / `housekeeping_ai_match_dev` | 同上 |
94
+ | `SKILL_BASE_DIR` | `backend/config/services/skill/` | 同上 |
95
+ | `SKILL_LOG_DIR` | `/data/skill-logs` | 同上 |
96
+
97
+ ## License
98
+
99
+ MIT
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "skill_self_evolution"
7
+ version = "0.2.0"
8
+ description = "Skill 自进化框架:Pydantic 全链路校验 + 规则执行 + AI 常识判断 + 离线进化的可插拔 Skill 执行引擎"
9
+ requires-python = ">=3.11"
10
+ dependencies = [
11
+ "pydantic>=2.0",
12
+ "pyyaml>=6.0",
13
+ "httpx>=0.25",
14
+ "pymysql>=1.1",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = [
19
+ "pytest>=8.0",
20
+ "pytest-asyncio>=0.23",
21
+ ]
22
+
23
+ [tool.setuptools]
24
+ package-dir = { "" = "src" }
25
+ packages = ["skill_self_evolution"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,27 @@
1
+ """
2
+ Skill Engine — 自进化框架核心。
3
+
4
+ 规则执行 → AI 常识判断 → 合理就过 / 不合理 AI 从原始数据重选 → 日志记录 → 离线进化 → 优化配置
5
+ """
6
+
7
+ from skill_self_evolution.executor import SkillExecutor
8
+ from skill_self_evolution.models import SkillInput, SkillOutput
9
+ from skill_self_evolution.context import get_trace_id, set_trace_id
10
+ from skill_self_evolution.logger import SkillLogger
11
+ from skill_self_evolution.fallback import FallbackStrategy
12
+ from skill_self_evolution.config_loader import ConfigVersionManager
13
+ from skill_self_evolution.evolver import Evolver
14
+ from skill_self_evolution.ai_assisted_executor import AiAssistedExecutor
15
+
16
+ __all__ = [
17
+ "SkillExecutor",
18
+ "SkillInput",
19
+ "SkillOutput",
20
+ "get_trace_id",
21
+ "set_trace_id",
22
+ "SkillLogger",
23
+ "FallbackStrategy",
24
+ "ConfigVersionManager",
25
+ "Evolver",
26
+ "AiAssistedExecutor",
27
+ ]
@@ -0,0 +1,244 @@
1
+ """
2
+ AiAssistedExecutor — 通用 AI 辅助执行基类。
3
+
4
+ 提供「验证 + 重选」的通用流程模板,Skill 只需定义:
5
+ - ai_validate():AI 常识判断(correction 角色) → AiValidationResult
6
+ - ai_reselect():AI 重新选择/提取(correction 角色) → AiReselectionResult
7
+ - ai_enhance():AI 增强(enhancement 角色) → dict | None
8
+
9
+ 框架自动处理:降级、熔断、is_failure 计算、JSONL 日志。
10
+ """
11
+
12
+ import logging
13
+ import time
14
+ from abc import ABC, abstractmethod
15
+ from typing import Any
16
+
17
+ from skill_self_evolution.context import set_trace_id
18
+ from skill_self_evolution.fallback import FallbackConfig, FallbackResult, FallbackStrategy
19
+ from skill_self_evolution.loader import SkillModule
20
+ from skill_self_evolution.logger import SkillLogger
21
+ from skill_self_evolution.models import (
22
+ AiReselectionResult,
23
+ AiValidationResult,
24
+ SkillInput,
25
+ SkillOutput,
26
+ )
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ class AiAssistedExecutor(ABC):
32
+ """AI 辅助执行基类。
33
+
34
+ 子类只需实现 2-3 个抽象方法,框架自动处理流程编排和降级。
35
+ """
36
+
37
+ @abstractmethod
38
+ async def ai_validate(
39
+ self,
40
+ skill_input: SkillInput,
41
+ rule_output: SkillOutput,
42
+ prompt_config: dict | None,
43
+ ) -> AiValidationResult:
44
+ """AI 常识判断(correction 角色)。
45
+
46
+ Returns:
47
+ AiValidationResult: result=合理/不合理 + reason
48
+ """
49
+
50
+ @abstractmethod
51
+ async def ai_reselect(
52
+ self,
53
+ skill_input: SkillInput,
54
+ rule_output: SkillOutput,
55
+ prompt_config: dict | None,
56
+ ) -> AiReselectionResult:
57
+ """AI 重新选择/提取(correction 角色)。
58
+
59
+ Returns:
60
+ AiReselectionResult: result=重选值(或"不合理") + reason
61
+ """
62
+
63
+ async def ai_enhance(
64
+ self,
65
+ skill_input: SkillInput,
66
+ rule_output: SkillOutput,
67
+ prompt_config: dict | None,
68
+ ) -> dict | None:
69
+ """AI 增强(enhancement 角色)。默认不增强,子类按需覆盖。"""
70
+ return None
71
+
72
+ async def run_correction_flow(
73
+ self,
74
+ skill: SkillModule,
75
+ skill_input: SkillInput,
76
+ rule_output: SkillOutput,
77
+ prompt_config: dict | None,
78
+ fallback: FallbackStrategy,
79
+ ) -> SkillOutput:
80
+ """correction 角色完整流程:验证 → 重选。"""
81
+ t0 = time.monotonic()
82
+ warnings: list[str] = list(rule_output.warnings) if rule_output.warnings else []
83
+
84
+ # 1. 熔断检查
85
+ fb_check = fallback.check_before_ai()
86
+ if fb_check.skip_ai:
87
+ warnings.extend(fb_check.warnings)
88
+ elapsed = (time.monotonic() - t0) * 1000
89
+ rule_output.warnings = warnings
90
+ self._log(skill, skill_input, rule_output, None, None, rule_output, warnings, elapsed)
91
+ return rule_output
92
+
93
+ # 2. AI 验证
94
+ ai_validation: AiValidationResult | None = None
95
+ try:
96
+ ai_validation = await self.ai_validate(skill_input, rule_output, prompt_config)
97
+ rule_output.ai_validated = True
98
+ except Exception as e:
99
+ logger.warning("Skill [%s] AI 验证异常: %s", skill.skill_name, e)
100
+ fb_result = fallback.on_validate_failure(e)
101
+ warnings.extend(fb_result.warnings)
102
+ if fb_result.skip_ai:
103
+ elapsed = (time.monotonic() - t0) * 1000
104
+ rule_output.ai_validated = False
105
+ rule_output.warnings = warnings
106
+ is_failure = self._is_failure(skill.ai_role, rule_output, None, None)
107
+ self._log(skill, skill_input, rule_output, None, None, rule_output, warnings, elapsed)
108
+ return rule_output
109
+
110
+ # 3. 验证不合理 → 重选
111
+ if ai_validation and ai_validation.result == "不合理":
112
+ fb_reselect = fallback.check_before_ai()
113
+ if fb_reselect.skip_ai:
114
+ warnings.extend(fb_reselect.warnings)
115
+ elapsed = (time.monotonic() - t0) * 1000
116
+ is_failure = self._is_failure(skill.ai_role, rule_output, ai_validation, None)
117
+ rule_output.warnings = warnings
118
+ self._log(skill, skill_input, rule_output, ai_validation, None, rule_output, warnings, elapsed)
119
+ return rule_output
120
+
121
+ try:
122
+ ai_reselection = await self.ai_reselect(skill_input, rule_output, prompt_config)
123
+ if ai_reselection and ai_reselection.result != "不合理":
124
+ final_result = ai_reselection.result
125
+ final_output = SkillOutput(
126
+ source="ai",
127
+ result=final_result if isinstance(final_result, dict) else {"value": final_result},
128
+ ai_validated=True,
129
+ ai_reselected=True,
130
+ warnings=warnings,
131
+ )
132
+ elapsed = (time.monotonic() - t0) * 1000
133
+ is_failure = self._is_failure(skill.ai_role, rule_output, ai_validation, ai_reselection)
134
+ self._log(skill, skill_input, rule_output, ai_validation, ai_reselection, final_output, warnings, elapsed)
135
+ return final_output
136
+ else:
137
+ warnings.append("AI 重选后仍不合理")
138
+ rule_output.ai_reselected = True
139
+ except Exception as e:
140
+ logger.warning("Skill [%s] AI 重选异常: %s", skill.skill_name, e)
141
+ fb_result2 = fallback.on_reselect_failure(e)
142
+ warnings.extend(fb_result2.warnings)
143
+
144
+ # 4. 验证合理或重选失败 → 返回规则结果
145
+ rule_output.warnings = warnings
146
+ elapsed = (time.monotonic() - t0) * 1000
147
+ is_failure = self._is_failure(skill.ai_role, rule_output, ai_validation, None)
148
+ self._log(skill, skill_input, rule_output, ai_validation, None, rule_output, warnings, elapsed)
149
+ return rule_output
150
+
151
+ async def run_enhancement_flow(
152
+ self,
153
+ skill: SkillModule,
154
+ skill_input: SkillInput,
155
+ rule_output: SkillOutput,
156
+ prompt_config: dict | None,
157
+ fallback: FallbackStrategy,
158
+ ) -> SkillOutput:
159
+ """enhancement 角色流程:规则 + AI 增强。"""
160
+ t0 = time.monotonic()
161
+ warnings: list[str] = list(rule_output.warnings) if rule_output.warnings else []
162
+
163
+ fb_check = fallback.check_before_ai()
164
+ if fb_check.skip_ai:
165
+ warnings.extend(fb_check.warnings)
166
+ elapsed = (time.monotonic() - t0) * 1000
167
+ rule_output.warnings = warnings
168
+ self._log(skill, skill_input, rule_output, None, None, rule_output, warnings, elapsed)
169
+ return rule_output
170
+
171
+ try:
172
+ ai_result = await self.ai_enhance(skill_input, rule_output, prompt_config)
173
+ merged = {**rule_output.result} if isinstance(rule_output.result, dict) else {}
174
+ if ai_result:
175
+ merged.update(ai_result)
176
+ final_output = SkillOutput(
177
+ source=rule_output.source,
178
+ result=merged,
179
+ ai_validated=True,
180
+ ai_reselected=False,
181
+ warnings=warnings,
182
+ )
183
+ elapsed = (time.monotonic() - t0) * 1000
184
+ self._log(skill, skill_input, rule_output, None, None, final_output, warnings, elapsed)
185
+ return final_output
186
+ except Exception as e:
187
+ logger.warning("Skill [%s] AI 增强异常: %s", skill.skill_name, e)
188
+ elapsed = (time.monotonic() - t0) * 1000
189
+ rule_output.warnings = warnings
190
+ self._log(skill, skill_input, rule_output, None, None, rule_output, warnings, elapsed)
191
+ return rule_output
192
+
193
+ @staticmethod
194
+ def _is_failure(
195
+ ai_role: str,
196
+ rule_output: SkillOutput,
197
+ ai_validation: AiValidationResult | None,
198
+ ai_reselection: AiReselectionResult | None,
199
+ ) -> bool:
200
+ """根据 ai_role 计算 is_failure(Pydantic 属性访问)。"""
201
+ if ai_role == "enhancement":
202
+ return False
203
+ if ai_role == "correction":
204
+ if ai_validation and ai_validation.result == "不合理":
205
+ return True
206
+ if ai_reselection and ai_reselection.result == "不合理":
207
+ return True
208
+ return False
209
+
210
+ def _log(
211
+ self,
212
+ skill: SkillModule,
213
+ skill_input: SkillInput,
214
+ rule_output: SkillOutput,
215
+ ai_validation: AiValidationResult | None,
216
+ ai_reselection: AiReselectionResult | None,
217
+ final_output: SkillOutput,
218
+ warnings: list[str],
219
+ elapsed_ms: float,
220
+ ) -> None:
221
+ """写入 JSONL 日志(Pydantic LogEntry 校验)。"""
222
+ try:
223
+ log_writer = SkillLogger(skill.skill_name)
224
+ if skill.summarize_input:
225
+ input_summary = skill.summarize_input(skill_input.input_data)
226
+ elif isinstance(skill_input.input_data, dict):
227
+ keys = list(skill_input.input_data.keys())[:5]
228
+ input_summary = {k: str(skill_input.input_data[k])[:100] for k in keys}
229
+ else:
230
+ input_summary = {"type": type(skill_input.input_data).__name__}
231
+
232
+ log_writer.log_execution(
233
+ trace_id=skill_input.trace_id or "",
234
+ is_failure=(ai_validation is not None and ai_validation.result == "不合理"),
235
+ input_summary=input_summary,
236
+ rule_output=rule_output.result,
237
+ ai_validation=ai_validation.model_dump() if ai_validation else None,
238
+ ai_reselection=ai_reselection.model_dump() if ai_reselection else None,
239
+ final_output=final_output.result,
240
+ warnings=warnings,
241
+ elapsed_ms=elapsed_ms,
242
+ )
243
+ except Exception as e:
244
+ logger.warning("Skill 日志记录失败: %s", e)
@@ -0,0 +1,87 @@
1
+ """
2
+ ?????? ? ? APP_ENV ?? DeepSeek API ???????
3
+
4
+ - local: DeepSeek ?? API (api.deepseek.com)
5
+ - test/prod: ??? MaaS (api.modelarts-maas.com/v2)
6
+ """
7
+
8
+ import os
9
+
10
+ from pydantic import BaseModel, Field
11
+
12
+ __all__ = ["DeepSeekEnvConfig", "DbConfig", "get_deepseek_config", "get_db_config"]
13
+
14
+
15
+ class DeepSeekEnvConfig(BaseModel):
16
+ """DeepSeek API ?????Pydantic ????"""
17
+
18
+ api_key: str = Field(default="", description="API Key")
19
+ api_base: str = Field(
20
+ default="https://api.deepseek.com/v1",
21
+ description="API ????",
22
+ )
23
+ model: str = Field(default="deepseek-chat", description="????")
24
+
25
+
26
+ class DbConfig(BaseModel):
27
+ """????????Pydantic ????"""
28
+
29
+ host: str = Field(default="127.0.0.1")
30
+ port: int = Field(default=3306, ge=1, le=65535)
31
+ user: str = Field(default="root")
32
+ password: str = Field(default="")
33
+ database: str = Field(default="housekeeping_ai_match_dev")
34
+
35
+
36
+ def _resolve_app_env() -> str:
37
+ return os.getenv("APP_ENV", "local").strip().lower()
38
+
39
+
40
+ def get_deepseek_config(
41
+ api_key: str = "",
42
+ api_base: str = "",
43
+ model: str = "",
44
+ ) -> DeepSeekEnvConfig:
45
+ env = _resolve_app_env()
46
+ if api_key and api_base:
47
+ return DeepSeekEnvConfig(
48
+ api_key=api_key,
49
+ api_base=api_base,
50
+ model=model or "deepseek-chat",
51
+ )
52
+ if env == "local":
53
+ key = api_key or os.getenv("DEEPSEEK_API_KEY", "")
54
+ base = api_base or os.getenv("DEEPSEEK_API_BASE", "https://api.deepseek.com/v1")
55
+ m = model or os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
56
+ return DeepSeekEnvConfig(api_key=key, api_base=base, model=m)
57
+ else:
58
+ key = api_key or os.getenv("WX_MATCH_DEEPSEEK_API_KEY", "")
59
+ base = api_base or os.getenv(
60
+ "WX_MATCH_DEEPSEEK_API_BASE", "https://api.modelarts-maas.com/v2"
61
+ )
62
+ m = model or os.getenv("WX_MATCH_AI_MODEL_NAME", "DeepSeek-V3.2")
63
+ return DeepSeekEnvConfig(api_key=key, api_base=base, model=m)
64
+
65
+
66
+ def get_db_config(
67
+ host: str = "",
68
+ port: int = 0,
69
+ user: str = "",
70
+ password: str = "",
71
+ database: str = "",
72
+ ) -> DbConfig:
73
+ if host and user and password:
74
+ return DbConfig(
75
+ host=host,
76
+ port=port or 3306,
77
+ user=user,
78
+ password=password,
79
+ database=database or "housekeeping_ai_match_dev",
80
+ )
81
+ return DbConfig(
82
+ host=host or os.getenv("DB_HOST", "127.0.0.1"),
83
+ port=port or int(os.getenv("DB_PORT", "3306")),
84
+ user=user or os.getenv("DB_USER", "root"),
85
+ password=password or os.getenv("DB_PASSWORD", ""),
86
+ database=database or os.getenv("DB_NAME", "housekeeping_ai_match_dev"),
87
+ )