agent-relationship 0.2.0__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.
Files changed (33) hide show
  1. agent_relationship/__init__.py +41 -0
  2. agent_relationship/__pycache__/__init__.cpython-311.pyc +0 -0
  3. agent_relationship/__pycache__/engine.cpython-311.pyc +0 -0
  4. agent_relationship/__pycache__/models.cpython-311.pyc +0 -0
  5. agent_relationship/__pycache__/repair.cpython-311.pyc +0 -0
  6. agent_relationship/__pycache__/tracker.cpython-311.pyc +0 -0
  7. agent_relationship/__pycache__/types.cpython-311.pyc +0 -0
  8. agent_relationship/engine.py +326 -0
  9. agent_relationship/llm/__init__.py +17 -0
  10. agent_relationship/llm/__pycache__/__init__.cpython-311.pyc +0 -0
  11. agent_relationship/llm/__pycache__/anthropic.cpython-311.pyc +0 -0
  12. agent_relationship/llm/__pycache__/base.cpython-311.pyc +0 -0
  13. agent_relationship/llm/__pycache__/deepseek.cpython-311.pyc +0 -0
  14. agent_relationship/llm/__pycache__/mock.cpython-311.pyc +0 -0
  15. agent_relationship/llm/__pycache__/openai.cpython-311.pyc +0 -0
  16. agent_relationship/llm/anthropic.py +108 -0
  17. agent_relationship/llm/base.py +91 -0
  18. agent_relationship/llm/deepseek.py +51 -0
  19. agent_relationship/llm/mock.py +63 -0
  20. agent_relationship/llm/openai.py +83 -0
  21. agent_relationship/models.py +191 -0
  22. agent_relationship/repair.py +77 -0
  23. agent_relationship/tests/__init__.py +0 -0
  24. agent_relationship/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  25. agent_relationship/tests/__pycache__/test_core.cpython-311-pytest-9.0.2.pyc +0 -0
  26. agent_relationship/tests/test_core.py +684 -0
  27. agent_relationship/tracker.py +453 -0
  28. agent_relationship/types.py +150 -0
  29. agent_relationship-0.2.0.dist-info/METADATA +421 -0
  30. agent_relationship-0.2.0.dist-info/RECORD +33 -0
  31. agent_relationship-0.2.0.dist-info/WHEEL +5 -0
  32. agent_relationship-0.2.0.dist-info/licenses/LICENSE +21 -0
  33. agent_relationship-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,41 @@
1
+ """
2
+ Agent Relationship — 多 Agent 系统的关系感知 Skill。
3
+
4
+ 给任何多 Agent 系统注入「社会层」:关系建模、Moloch 检测、关系修复。
5
+
6
+ 用法:
7
+ from agent_relationship import RelationshipTracker
8
+
9
+ tracker = RelationshipTracker()
10
+ tracker.track("alice", "bob", {"action": "help", "result": "success"})
11
+ h = tracker.health("alice", "bob")
12
+ print(tracker.summary())
13
+
14
+ 四引擎:
15
+ - "mock" — 零配置,确定性模拟
16
+ - "openai" — GPT / Codex
17
+ - "deepseek" — V4 Flash / V4 Pro
18
+ - "anthropic" — Claude / Claude Code
19
+ """
20
+
21
+ from .tracker import RelationshipTracker
22
+ from .types import (
23
+ TrackResult,
24
+ Health,
25
+ NetworkReport,
26
+ MolochReport,
27
+ MolochZone,
28
+ RepairPath,
29
+ )
30
+
31
+ __all__ = [
32
+ "RelationshipTracker",
33
+ "TrackResult",
34
+ "Health",
35
+ "NetworkReport",
36
+ "MolochReport",
37
+ "MolochZone",
38
+ "RepairPath",
39
+ ]
40
+
41
+ __version__ = "0.2.0"
@@ -0,0 +1,326 @@
1
+ """
2
+ Agent Relationship — 关系引擎
3
+
4
+ 管理所有 Agent 之间的关系场:记录交互、评估健康、检测危机。
5
+ """
6
+
7
+ import time
8
+ from typing import Dict, List, Optional, Set, Tuple
9
+
10
+ from .models import RelationProfile, InteractionRecord
11
+ from .repair import RepairMechanism
12
+ from .llm import LLMBackend
13
+
14
+
15
+ class RelationshipEngine:
16
+ """
17
+ 关系引擎 — 多 Agent 系统的社会层。
18
+
19
+ 核心功能:
20
+ - 记录交互 → 更新 balance / trust / type
21
+ - 查询双边关系健康度
22
+ - Moloch 竞争升级检测 (BFS 连通分量 + 趋势)
23
+ - 关系修复路径
24
+ """
25
+
26
+ def __init__(self, llm: LLMBackend, max_history: int = 100):
27
+ self.llm = llm
28
+ self.profiles: Dict[Tuple[str, str], RelationProfile] = {}
29
+ self.repair = RepairMechanism()
30
+ self.moloch_threshold: float = 0.3
31
+ self.moloch_min_zone_size: int = 3
32
+ self._previous_moloch_zones: List[Dict] = []
33
+ self.max_history = max_history
34
+
35
+ def _key(self, a: str, b: str) -> Tuple[str, str]:
36
+ """规范化键 — (a,b) 和 (b,a) 指向同一 profile"""
37
+ return (a, b) if a < b else (b, a)
38
+
39
+ # ── 查询 ──
40
+
41
+ def get_relationship(
42
+ self, agent_a: str, agent_b: str
43
+ ) -> Optional[RelationProfile]:
44
+ """查询双边关系"""
45
+ return self.profiles.get(self._key(agent_a, agent_b))
46
+
47
+ def get_or_create_relationship(
48
+ self, agent_a: str, agent_b: str
49
+ ) -> RelationProfile:
50
+ """查询或创建双边关系"""
51
+ key = self._key(agent_a, agent_b)
52
+ if key not in self.profiles:
53
+ self.profiles[key] = RelationProfile(
54
+ agent_a=key[0], agent_b=key[1],
55
+ max_history_length=self.max_history,
56
+ )
57
+ return self.profiles[key]
58
+
59
+ # ── 记录交互 ──
60
+
61
+ def record_interaction(
62
+ self,
63
+ initiator: str,
64
+ target: str,
65
+ interaction_data: Dict,
66
+ ) -> Dict:
67
+ """
68
+ 记录一次交互,更新关系档案。
69
+
70
+ interaction_data 自由格式:
71
+ {"action": "...", "result": "...", "narrative": "..."}
72
+ """
73
+ impact_a = self._estimate_impact(
74
+ initiator, interaction_data, True
75
+ )
76
+ impact_b = self._estimate_impact(
77
+ target, interaction_data, False
78
+ )
79
+
80
+ record = InteractionRecord(
81
+ timestamp=time.time(),
82
+ interaction_type=interaction_data.get(
83
+ "action", interaction_data.get("type", "unknown")
84
+ ),
85
+ initiator=initiator,
86
+ target=target,
87
+ action=interaction_data,
88
+ impact_a=impact_a,
89
+ impact_b=impact_b,
90
+ context=interaction_data,
91
+ )
92
+
93
+ profile = self.get_or_create_relationship(initiator, target)
94
+ profile.record_interaction(record)
95
+
96
+ return {
97
+ "balance": profile.balance,
98
+ "trust": profile.trust,
99
+ "relation_type": profile.relation_type.value,
100
+ "impact": (impact_a + impact_b) / 2,
101
+ "interaction_count": profile.interaction_count,
102
+ }
103
+
104
+ # ── 协作检查 ──
105
+
106
+ def can_cooperate(
107
+ self,
108
+ agent_a: str,
109
+ agent_b: str,
110
+ threshold: float = 0.4,
111
+ is_repair_attempt: bool = False,
112
+ ) -> Dict:
113
+ """
114
+ 检查两个 Agent 是否可以协作。
115
+
116
+ 不是简单的是/否 — 返回修复路径而非永久封锁。
117
+ """
118
+ profile = self.get_relationship(agent_a, agent_b)
119
+
120
+ if not profile:
121
+ return {
122
+ "can": True,
123
+ "reason": "无历史交互",
124
+ "confidence": 0.3,
125
+ "needs_repair": False,
126
+ }
127
+
128
+ if is_repair_attempt:
129
+ can_repair, msg = self.repair.can_attempt_repair(profile)
130
+ if can_repair:
131
+ result = self.repair.execute_repair_attempt(
132
+ profile
133
+ )
134
+ return {
135
+ "can": True,
136
+ "reason": f"修复尝试: {msg}",
137
+ "confidence": result["temp_threshold"],
138
+ "needs_repair": True,
139
+ }
140
+ else:
141
+ return {
142
+ "can": False,
143
+ "reason": msg,
144
+ "confidence": 0.0,
145
+ "needs_repair": True,
146
+ }
147
+
148
+ if profile.balance > threshold:
149
+ return {
150
+ "can": True,
151
+ "reason": f"关系良好 (balance={profile.balance:.2f})",
152
+ "confidence": profile.balance,
153
+ "needs_repair": False,
154
+ }
155
+ else:
156
+ return {
157
+ "can": False,
158
+ "reason": (
159
+ f"关系需要修复 (balance={profile.balance:.2f})"
160
+ ),
161
+ "confidence": 1.0 - profile.balance,
162
+ "needs_repair": True,
163
+ "repair_paths": [
164
+ p["description"]
165
+ for p in self.repair.available_paths()
166
+ ],
167
+ }
168
+
169
+ # ── 影响评估 ──
170
+
171
+ def _estimate_impact(
172
+ self, agent_id: str, interaction: Dict, is_initiator: bool
173
+ ) -> float:
174
+ """通过 LLM 估算交互影响"""
175
+ return self.llm.estimate_impact(
176
+ agent_id, interaction, is_initiator
177
+ )
178
+
179
+ # ── Moloch 检测 ──
180
+
181
+ def _detect_moloch_zones(self) -> List[Dict]:
182
+ """
183
+ Moloch 萌芽检测。
184
+
185
+ 1. 收集 balance < threshold 的边
186
+ 2. BFS 找连通分量
187
+ 3. 过滤 ≥ min_zone_size 的分量
188
+ 4. 计算平均 balance / 严重程度 / 趋势
189
+ """
190
+ threshold = self.moloch_threshold
191
+ min_size = self.moloch_min_zone_size
192
+
193
+ imbalanced_edges = [
194
+ (a, b)
195
+ for (a, b), profile in self.profiles.items()
196
+ if profile.balance < threshold
197
+ and profile.interaction_count >= 3
198
+ ]
199
+
200
+ # 建图
201
+ graph: Dict[str, Set[str]] = {}
202
+ for a, b in imbalanced_edges:
203
+ graph.setdefault(a, set()).add(b)
204
+ graph.setdefault(b, set()).add(a)
205
+
206
+ moloch_zones = []
207
+ visited: Set[str] = set()
208
+
209
+ for node in graph:
210
+ if node in visited:
211
+ continue
212
+
213
+ component: Set[str] = set()
214
+ queue = [node]
215
+ while queue:
216
+ current = queue.pop(0)
217
+ if current in visited:
218
+ continue
219
+ visited.add(current)
220
+ component.add(current)
221
+ for neighbor in graph.get(current, set()):
222
+ if neighbor not in visited:
223
+ queue.append(neighbor)
224
+
225
+ if len(component) >= min_size:
226
+ zone_balances: List[float] = []
227
+ for a, b in imbalanced_edges:
228
+ if a in component and b in component:
229
+ profile = self.profiles.get((a, b))
230
+ if profile:
231
+ zone_balances.append(profile.balance)
232
+
233
+ avg_balance = (
234
+ sum(zone_balances) / len(zone_balances)
235
+ if zone_balances
236
+ else 0.0
237
+ )
238
+
239
+ trend = "stable"
240
+ prev = self._find_previous_zone(component)
241
+ if prev:
242
+ delta = avg_balance - prev["avg_balance"]
243
+ if delta < -0.05:
244
+ trend = "deteriorating"
245
+ elif delta > 0.05:
246
+ trend = "improving"
247
+
248
+ moloch_zones.append(
249
+ {
250
+ "agents": sorted(component),
251
+ "size": len(component),
252
+ "avg_balance": avg_balance,
253
+ "trend": trend,
254
+ "severity": self._classify_severity(
255
+ avg_balance, len(component), trend
256
+ ),
257
+ "first_detected": prev is None,
258
+ }
259
+ )
260
+
261
+ self._previous_moloch_zones = moloch_zones
262
+ return moloch_zones
263
+
264
+ def get_moloch_report(self) -> Dict:
265
+ """获取 Moloch 检测报告"""
266
+ zones = self._detect_moloch_zones()
267
+ total = len(self.profiles)
268
+ balanced = sum(
269
+ 1
270
+ for p in self.profiles.values()
271
+ if p.balance >= self.moloch_threshold
272
+ )
273
+ return {
274
+ "zones": zones,
275
+ "active": len(zones) > 0,
276
+ "total_relationships": total,
277
+ "balanced_ratio": balanced / max(total, 1),
278
+ }
279
+
280
+ def _find_previous_zone(
281
+ self, component: Set[str]
282
+ ) -> Optional[Dict]:
283
+ comp_set = set(component)
284
+ for prev in self._previous_moloch_zones:
285
+ if set(prev["agents"]) == comp_set:
286
+ return prev
287
+ return None
288
+
289
+ @staticmethod
290
+ def _classify_severity(
291
+ avg_balance: float, size: int, trend: str
292
+ ) -> str:
293
+ if avg_balance < 0.1:
294
+ base = "critical"
295
+ elif avg_balance < 0.2:
296
+ base = "high"
297
+ elif avg_balance < 0.3:
298
+ base = "moderate"
299
+ else:
300
+ base = "watch"
301
+
302
+ if trend == "deteriorating" and base in ("moderate", "high"):
303
+ return f"{base}_escalating"
304
+ if size >= 10 and trend == "deteriorating":
305
+ return f"{base}_large_scale"
306
+
307
+ return base
308
+
309
+ # ── 聚合 ──
310
+
311
+ def average_balance(self) -> float:
312
+ """全局平均 balance"""
313
+ if not self.profiles:
314
+ return 0.5
315
+ return (
316
+ sum(p.balance for p in self.profiles.values())
317
+ / len(self.profiles)
318
+ )
319
+
320
+ def all_agent_ids(self) -> List[str]:
321
+ """获取所有已知的 Agent ID"""
322
+ ids: Set[str] = set()
323
+ for a, b in self.profiles:
324
+ ids.add(a)
325
+ ids.add(b)
326
+ return sorted(ids)
@@ -0,0 +1,17 @@
1
+ """
2
+ Agent Relationship — LLM 后端集合
3
+ """
4
+
5
+ from .base import LLMBackend
6
+ from .mock import MockLLM
7
+ from .openai import OpenAILLM
8
+ from .deepseek import DeepSeekLLM
9
+ from .anthropic import AnthropicLLM
10
+
11
+ __all__ = [
12
+ "LLMBackend",
13
+ "MockLLM",
14
+ "OpenAILLM",
15
+ "DeepSeekLLM",
16
+ "AnthropicLLM",
17
+ ]
@@ -0,0 +1,108 @@
1
+ """
2
+ Agent Relationship — AnthropicLLM
3
+
4
+ Anthropic Claude API 后端 — Messages API 格式。
5
+
6
+ 环境变量:
7
+ ANTHROPIC_API_KEY — API 密钥 (必填)
8
+ ANTHROPIC_MODEL — 模型名 (默认 claude-sonnet-4-20250514)
9
+
10
+ 可用模型:
11
+ - claude-sonnet-4-20250514: 平衡性能与成本 (默认)
12
+ - claude-opus-4-20250918: 最强推理
13
+ - claude-haiku-4-20250514: 最快最便宜
14
+
15
+ 与 OpenAI 格式的核心差异:
16
+ - 端点: https://api.anthropic.com/v1/messages
17
+ - 认证: x-api-key 头 (非 Authorization: Bearer)
18
+ - 版本: anthropic-version: 2023-06-01 (必填)
19
+ - system: 顶层字段 (非 messages 数组内)
20
+ - 响应: content[0].text (非 choices[0].message.content)
21
+ - max_tokens: 必填 (OpenAI 可选)
22
+ - JSON mode: 无原生支持,用 prompt 引导
23
+ """
24
+
25
+ import json
26
+ import os
27
+ import urllib.request
28
+ import urllib.error
29
+ from typing import Optional
30
+ from .base import LLMBackend
31
+
32
+
33
+ class AnthropicLLM(LLMBackend):
34
+ """
35
+ Anthropic Claude API 后端 — Messages API。
36
+
37
+ 使用 urllib 直调 HTTP,无需 anthropic SDK。
38
+ """
39
+
40
+ ANTHROPIC_VERSION = "2023-06-01"
41
+ ENDPOINT = "https://api.anthropic.com/v1/messages"
42
+
43
+ def __init__(
44
+ self,
45
+ api_key: Optional[str] = None,
46
+ model: str = "claude-sonnet-4-20250514",
47
+ ):
48
+ self.api_key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
49
+ self.model = os.environ.get("ANTHROPIC_MODEL", model)
50
+
51
+ if not self.api_key:
52
+ raise ValueError(
53
+ "ANTHROPIC_API_KEY 未设置。"
54
+ "请设置环境变量或传入 api_key 参数。"
55
+ )
56
+
57
+ def chat(
58
+ self,
59
+ prompt: str,
60
+ system: str = "",
61
+ temperature: float = 0.3,
62
+ max_tokens: int = 500,
63
+ json_mode: bool = False,
64
+ ) -> str:
65
+ # 构建请求体
66
+ body: dict = {
67
+ "model": self.model,
68
+ "max_tokens": max_tokens,
69
+ "messages": [{"role": "user", "content": prompt}],
70
+ }
71
+
72
+ # Anthropic: system 是顶层字段
73
+ system_content = system or ""
74
+ if json_mode:
75
+ # Anthropic 无原生 JSON mode,通过 system prompt 引导
76
+ json_instruction = (
77
+ "\n\n你必须返回纯 JSON,只输出 JSON 对象,"
78
+ "不要包含 markdown 代码块标记 (```json```),"
79
+ "不要添加任何解释文字。"
80
+ )
81
+ system_content += json_instruction
82
+
83
+ if system_content.strip():
84
+ body["system"] = system_content.strip()
85
+
86
+ if temperature is not None:
87
+ body["temperature"] = temperature
88
+
89
+ req = urllib.request.Request(
90
+ self.ENDPOINT,
91
+ data=json.dumps(body).encode("utf-8"),
92
+ headers={
93
+ "Content-Type": "application/json",
94
+ "x-api-key": self.api_key,
95
+ "anthropic-version": self.ANTHROPIC_VERSION,
96
+ },
97
+ )
98
+ try:
99
+ with urllib.request.urlopen(req, timeout=60) as resp:
100
+ data = json.loads(resp.read())
101
+ # Anthropic 响应: content 是数组,取第一个 text
102
+ return data["content"][0]["text"]
103
+ except urllib.error.HTTPError as e:
104
+ raise RuntimeError(
105
+ f"Anthropic API 错误 ({e.code}): {e.read().decode()}"
106
+ )
107
+ except Exception as e:
108
+ raise RuntimeError(f"Anthropic 调用失败: {e}")
@@ -0,0 +1,91 @@
1
+ """
2
+ Agent Relationship — LLM 抽象基类
3
+
4
+ 所有 LLM 后端必须实现的接口。
5
+ """
6
+
7
+ import json
8
+ from abc import ABC, abstractmethod
9
+
10
+
11
+ class LLMBackend(ABC):
12
+ """LLM 后端抽象基类"""
13
+
14
+ @abstractmethod
15
+ def chat(
16
+ self,
17
+ prompt: str,
18
+ system: str = "",
19
+ temperature: float = 0.3,
20
+ max_tokens: int = 500,
21
+ json_mode: bool = False,
22
+ ) -> str:
23
+ """通用对话接口"""
24
+ ...
25
+
26
+ # ── estimate_impact ──
27
+
28
+ def estimate_impact(
29
+ self,
30
+ agent_id: str,
31
+ interaction: dict,
32
+ is_initiator: bool,
33
+ ) -> float:
34
+ """
35
+ 评估交互对 Agent 的影响 → (-1.0, +1.0)
36
+
37
+ 优先用 LLM 语义评估,失败时降级到启发式。
38
+ """
39
+ role = "发起方" if is_initiator else "接收方"
40
+ prompt = (
41
+ f"你是 {agent_id} ({role})。评估以下交互对你的影响:\n\n"
42
+ f"交互详情: {json.dumps(interaction, ensure_ascii=False)}\n\n"
43
+ f'返回 JSON:\n'
44
+ f'{{"impact": <float -1到1>, '
45
+ f'"reasoning": "<一句话分析>", '
46
+ f'"fairness": <float 0-1>}}'
47
+ )
48
+ try:
49
+ result = json.loads(self.chat(prompt, json_mode=True))
50
+ return max(-1.0, min(1.0, float(result.get("impact", 0.0))))
51
+ except Exception:
52
+ return self._fallback_estimate_impact(interaction, is_initiator)
53
+
54
+ @staticmethod
55
+ def _fallback_estimate_impact(
56
+ interaction: dict, is_initiator: bool
57
+ ) -> float:
58
+ """LLM 不可用时的启发式降级"""
59
+ atype = interaction.get("action", interaction.get("type", ""))
60
+ details = interaction.get("details", interaction.get("metadata", {}))
61
+
62
+ beneficial = {
63
+ "resource_exchange": 0.3,
64
+ "collaboration": 0.4,
65
+ "collaborate": 0.4,
66
+ "knowledge_share": 0.35,
67
+ "help": 0.5,
68
+ "delegate_task": 0.3,
69
+ "handoff": 0.2,
70
+ }
71
+ harmful = {
72
+ "attack": -0.5,
73
+ "deception": -0.4,
74
+ "theft": -0.6,
75
+ "sabotage": -0.7,
76
+ }
77
+
78
+ if atype in beneficial:
79
+ return beneficial[atype]
80
+ if atype in harmful:
81
+ return harmful[atype]
82
+
83
+ fair = details.get("fair_price", 0)
84
+ actual = details.get("price", details.get("amount", 0))
85
+ if fair > 0:
86
+ return (
87
+ 0.3
88
+ if (actual <= fair if is_initiator else actual >= fair)
89
+ else -0.1
90
+ )
91
+ return 0.1
@@ -0,0 +1,51 @@
1
+ """
2
+ Agent Relationship — DeepSeekLLM
3
+
4
+ DeepSeek API 后端 — OpenAI 兼容接口。
5
+ 默认 deepseek-v4-flash (无推理模式,适合 Agent 交互)。
6
+
7
+ 环境变量:
8
+ DEEPSEEK_API_KEY — API 密钥 (必填)
9
+ DEEPSEEK_BASE_URL — API 端点 (默认 https://api.deepseek.com)
10
+ DEEPSEEK_MODEL — 模型名 (默认 deepseek-v4-flash)
11
+
12
+ 可用模型:
13
+ - deepseek-v4-flash: 高吞吐、无推理模式、成本低 (默认)
14
+ - deepseek-v4-pro: 复杂推理、内置思维链 — 注意: 可能因
15
+ thinking 消耗 token 导致 content 为空
16
+ """
17
+
18
+ import os
19
+ from typing import Optional
20
+ from .base import LLMBackend
21
+ from .openai import OpenAILLM
22
+
23
+
24
+ class DeepSeekLLM(OpenAILLM):
25
+ """
26
+ DeepSeek API — OpenAI 兼容但独立端点。
27
+
28
+ 与 OpenAI 的核心差异:
29
+ - 端点: https://api.deepseek.com (无 /v1 后缀)
30
+ - 上下文: 1M tokens
31
+ - 定价: ~$0.28/$1.10 per 1M input/output
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ api_key: Optional[str] = None,
37
+ base_url: Optional[str] = None,
38
+ model: str = "deepseek-v4-flash",
39
+ ):
40
+ # 不走 OpenAILLM.__init__,避免 OPENAI_MODEL 环境变量覆盖
41
+ LLMBackend.__init__(self)
42
+ _api_key = api_key or os.environ.get("DEEPSEEK_API_KEY", "")
43
+ if not _api_key:
44
+ raise ValueError(
45
+ "DEEPSEEK_API_KEY 未设置。请设置环境变量或传入 api_key 参数。"
46
+ )
47
+ self.api_key = _api_key
48
+ self.base_url = base_url or os.environ.get(
49
+ "DEEPSEEK_BASE_URL", "https://api.deepseek.com"
50
+ )
51
+ self.model = os.environ.get("DEEPSEEK_MODEL", model)