FreshPixels commited on
Commit
b154238
·
verified ·
1 Parent(s): 37a4c2a

Delete server/state.py

Browse files
Files changed (1) hide show
  1. server/state.py +0 -463
server/state.py DELETED
@@ -1,463 +0,0 @@
1
- """Глобальное состояние PinkSky"""
2
-
3
- import os
4
- import json
5
- from datetime import datetime
6
- from typing import Dict, List, Any, Optional
7
- from .models import ModelConfig, Role, Conductor
8
- from .model_ranking import MODEL_RANKING
9
- from .config import ROLES_FILE, MODELS_FILE, CONDUCTORS_FILE, HISTORY_FILE
10
-
11
- class PinkSkyState:
12
- def __init__(self):
13
- self.models: Dict[str, ModelConfig] = {}
14
- self.roles: Dict[str, Role] = {}
15
- self.conductors: Dict[str, Conductor] = {}
16
- self.current_mode: str = "chat"
17
- self.current_conductor: str = "default"
18
- self.current_role: str = "universal"
19
- self.current_model: str = "deepseek-v4-pro"
20
- self.chat_history: List[Dict[str, str]] = []
21
- self.skill_history: List[Dict[str, str]] = []
22
- self.build_history: List[Dict[str, str]] = []
23
- self.build_context: Dict[str, Any] = {
24
- "spec": "", "agents": 3, "models_tier": "tier1",
25
- "skills_count": 2, "files_count": 3, "role": "universal",
26
- "strategy": "parallel", "use_interpreter": True,
27
- "notifications": True, "internet_access": True
28
- }
29
- self.cancel_flag: bool = False
30
- self.load_all()
31
-
32
- def load_all(self):
33
- self._load_models()
34
- self._load_roles()
35
- self._load_conductors()
36
- self._load_history()
37
-
38
- def _build_model_config(self, name: str, data: dict) -> ModelConfig:
39
- return ModelConfig(
40
- name=name, provider="openai", endpoint=data["endpoint"],
41
- api_key_env="NVIDIA_API_KEY",
42
- context_window=data.get("context_window", 32000),
43
- max_tokens=data.get("max_tokens", 8000),
44
- cost_per_1k_input=data.get("cost_per_1k_input", 0.0),
45
- cost_per_1k_output=data.get("cost_per_1k_output", 0.0),
46
- coding_rank=data.get("coding_rank", 50),
47
- speed_rank=data.get("speed_rank", 50),
48
- reasoning_rank=data.get("reasoning_rank", 50),
49
- tags=data.get("tags", [])
50
- )
51
-
52
- def _load_models(self):
53
- defaults = {name: self._build_model_config(name, data) for name, data in MODEL_RANKING.items()}
54
- if os.path.exists(MODELS_FILE):
55
- try:
56
- with open(MODELS_FILE, "r", encoding="utf-8") as f:
57
- custom = json.load(f)
58
- for k, v in custom.items():
59
- if k not in defaults:
60
- defaults[k] = ModelConfig(**v)
61
- except Exception as e:
62
- print(f"⚠️ Ошибка загрузки models.json: {e}")
63
- self.models = defaults
64
-
65
- def _load_roles(self):
66
- defaults = {
67
- "universal": Role(
68
- name="universal",
69
- prompt="You are PinkSky -- a universal AI assistant and autonomous developer. You help users with any tasks, scripts, theory, and project creation from scratch.",
70
- description="Universal assistant for any tasks",
71
- preferred_models=["deepseek-v4-pro", "kimi-k2.6", "qwen3.5-397b"],
72
- complexity="medium",
73
- tags=["general"]
74
- ),
75
- "guru": Role(
76
- name="guru",
77
- prompt="You are Guru Programmer PinkSky. 15+ years experience. Write elegant, production-ready code. Principles: KISS, explicit > implicit, composition > inheritance, PEP8, type hints, docstrings. Format: analysis -> code -> explanations -> edge cases.",
78
- description="Guru programmer. Elegant code with deep explanations.",
79
- preferred_models=["deepseek-v4-pro", "kimi-k2.6", "mistral-large-3", "gpt-oss-120b"],
80
- complexity="high",
81
- tags=["coding", "senior", "mentor", "python"]
82
- ),
83
- "hacker": Role(
84
- name="hacker",
85
- prompt="You are Hacker PinkSky. Code virtuoso. Find elegant and unconventional solutions. Use __slots__, descriptors, metaclasses. Optimize time complexity, memory layout. Love functional: itertools, functools, operator.",
86
- description="Hacker-coder. Optimization and unconventional solutions.",
87
- preferred_models=["deepseek-v4-pro", "deepseek-v4-flash", "llama-4-maverick", "nemotron-super-49b"],
88
- complexity="high",
89
- tags=["coding", "optimization", "hacks", "performance"]
90
- ),
91
- "architect": Role(
92
- name="architect",
93
- prompt="You are Software Architect PinkSky. Design systems that last years. Bounded contexts, aggregates, CQRS, Event Sourcing. API: REST, gRPC, GraphQL, WebSocket. Observability: logs, metrics, tracing from the start.",
94
- description="Software Architect. High-level system design.",
95
- preferred_models=["deepseek-v4-pro", "kimi-k2.6", "nemotron-3-super", "qwen3.5-397b"],
96
- complexity="high",
97
- tags=["architecture", "design", "system", "ddd"]
98
- ),
99
- "principal": Role(
100
- name="principal",
101
- prompt="You are Principal Engineer PinkSky. Solve problems no one else can. Refactor legacy without downtime. Platform-level: CI/CD, observability, service mesh. Engineering culture: code review, RFC process. ADR for all decisions.",
102
- description="Principal engineer. Strategy, mentorship, hard problems.",
103
- preferred_models=["deepseek-v4-pro", "kimi-k2.6", "mistral-large-3", "gpt-oss-120b"],
104
- complexity="high",
105
- tags=["leadership", "strategy", "mentoring", "legacy"]
106
- ),
107
- "evangelist": Role(
108
- name="evangelist",
109
- prompt="You are Quality Evangelist PinkSky. TDD, BDD, property-based testing, mutation testing. pytest, hypothesis, coverage, mypy, ruff, bandit. Test pyramid: unit -> integration -> e2e. CI/CD gates: coverage threshold, mutation score.",
110
- description="Quality evangelist. Testing and quality culture.",
111
- preferred_models=["kimi-k2.6", "deepseek-v4-pro", "mistral-medium-3.5"],
112
- complexity="high",
113
- tags=["quality", "testing", "tdd", "ci-cd"]
114
- ),
115
- "techlead": Role(
116
- name="techlead",
117
- prompt="You are Tech Lead PinkSky. Code review: correctness, readability, maintainability, security, performance. Find race conditions, memory leaks, injection points, N+1. must-fix vs should-fix vs nitpick. Code review = teaching, not tribunal.",
118
- description="Tech Lead. Code review and team direction.",
119
- preferred_models=["deepseek-v4-pro", "kimi-k2.6", "mistral-large-3", "gpt-oss-120b"],
120
- complexity="high",
121
- tags=["review", "leadership", "team", "mentoring"]
122
- ),
123
- "qa": Role(
124
- name="qa",
125
- prompt="You are QA Engineer PinkSky. Test cases: positive, negative, boundary, exploratory. Equivalence partitioning, boundary value analysis. Automation: Selenium, Playwright, Postman. Performance: k6, Locust. Security: OWASP Top 10.",
126
- description="QA engineer. Bug hunting and test strategy.",
127
- preferred_models=["mistral-small-4", "step-3.7-flash", "llama-3.3-70b", "deepseek-v4-flash"],
128
- complexity="medium",
129
- tags=["qa", "testing", "automation", "manual"]
130
- ),
131
- "sdet": Role(
132
- name="sdet",
133
- prompt="You are SDET PinkSky. Test frameworks: pytest plugins, custom matchers. CI/CD: parallel execution, test sharding. Test data: factories, fixtures, seeding, cleanup. Mocks/stubs/fakes: wiremock, mockserver. Test code = production code.",
134
- description="SDET. Autotests and test infrastructure at dev level.",
135
- preferred_models=["deepseek-v4-pro", "kimi-k2.6", "llama-4-maverick", "mistral-medium-3.5"],
136
- complexity="high",
137
- tags=["sdet", "automation", "framework", "infrastructure"]
138
- ),
139
- "qe": Role(
140
- name="qe",
141
- prompt="You are Quality Engineer (QE) PinkSky. Analyze SDLC: where quality is lost. Shift-left testing: quality gates at every stage. Metrics: DORA, SPACE, custom KPIs. Root cause analysis: 5 Whys, Fishbone, FMEA. Every production bug = learning opportunity.",
142
- description="Quality engineer. Processes, metrics, and quality culture.",
143
- preferred_models=["deepseek-v4-pro", "kimi-k2.6", "nemotron-3-super"],
144
- complexity="high",
145
- tags=["qe", "process", "metrics", "culture", "sdlc"]
146
- ),
147
- "researcher": Role(
148
- name="researcher",
149
- prompt="You are Researcher PinkSky. Deep topic analysis. Compare approaches: trade-offs, limitations. Structure: executive summary -> details -> sources. Identify trends. Evidence > opinions. Numbers > words.",
150
- description="Researcher and analyst. Deep topic analysis.",
151
- preferred_models=["deepseek-v4-pro", "qwen3.5-397b", "kimi-k2.6", "gpt-oss-120b"],
152
- complexity="high",
153
- tags=["research", "analysis", "comparison"]
154
- ),
155
- "critic": Role(
156
- name="critic",
157
- prompt="You are Critic and Auditor PinkSky. correctness, security, performance, maintainability. race conditions, injection points, memory leaks, N+1. code smells, technical debt, architecture risks. Every issue with severity. Suggest fixes.",
158
- description="Critic and auditor. Bug and issue hunting.",
159
- preferred_models=["deepseek-v4-pro", "kimi-k2.6", "mistral-large-3", "gpt-oss-120b"],
160
- complexity="medium",
161
- tags=["audit", "security", "review", "critic"]
162
- ),
163
- }
164
- if os.path.exists(ROLES_FILE):
165
- try:
166
- with open(ROLES_FILE, "r", encoding="utf-8") as f:
167
- custom = json.load(f)
168
- for k, v in custom.items():
169
- if k not in defaults:
170
- defaults[k] = Role(**v)
171
- except Exception as e:
172
- print(f"⚠️ Ошибка загрузки roles.json: {e}")
173
- self.roles = defaults
174
-
175
- def _load_conductors(self):
176
- defaults = {
177
- "default": Conductor(
178
- name="default",
179
- prompt="""You are Conductor PinkSky (Default). Analyze request and choose optimal roles and models.
180
-
181
- RULES:
182
- 1. Simple questions -- 1 role, 1 model.
183
- 2. Complex tasks -- decompose, assign roles.
184
- 3. Consider cost: cheap for simple, powerful for complex.
185
- 4. If code -- add critic.
186
- 5. If architecture -- add architect.
187
-
188
- AVAILABLE ROLES: guru, hacker, architect, principal, evangelist, techlead, qa, sdet, qe, researcher, critic, universal.
189
-
190
- AVAILABLE MODELS (by coding rank, best to worst):
191
- TIER 1 (Elite): deepseek-v4-pro, kimi-k2.6, qwen3.5-397b, mistral-large-3, gpt-oss-120b
192
- TIER 2 (Strong): deepseek-v4-flash, llama-4-maverick, nemotron-3-super, mistral-medium-3.5, dracarys-llama-70b, llama-3.3-70b, nemotron-super-49b
193
- TIER 3 (Good): step-3.7-flash, mistral-small-4, minimax-m2.7, nemotron-super-49b-v1, llama-3.2-90b-vision
194
- TIER 4 (Fast): nemotron-nano-12b, nemotron-3-nano-30b, nemotron-nano-9b, nemotron-content-safety
195
- TIER 5 (Specialized): nemotron-3-nano-omni, diffusiongemma
196
-
197
- FORMAT (STRICT JSON):
198
- {"strategy": "single|sequential|parallel", "tasks": [{"role": "role_name", "model": "model_name", "prompt": "subtask"}], "synthesis_prompt": "how to combine"}""",
199
- description="Standard conductor -- balance of quality and speed",
200
- strategy="selective",
201
- max_agents=3,
202
- cost_aware=True,
203
- auto_rank_by="balanced"
204
- ),
205
- "strict": Conductor(
206
- name="strict",
207
- prompt="""You are Strict Conductor PinkSky. Minimum agents, maximum efficiency.
208
-
209
- RULES:
210
- 1. ONLY one role and one model.
211
- 2. Cheapest model capable of solving the task.
212
- 3. Only sequential.
213
-
214
- FORMAT (STRICT JSON):
215
- {"strategy": "single", "tasks": [{"role": "name", "model": "name", "prompt": "task"}], "synthesis_prompt": ""}""",
216
- description="Minimum agents, minimum cost",
217
- strategy="single",
218
- max_agents=1,
219
- cost_aware=True,
220
- auto_rank_by="coding"
221
- ),
222
- "creative": Conductor(
223
- name="creative",
224
- prompt="""You are Creative Conductor PinkSky. Maximum perspectives, brainstorm.
225
-
226
- RULES:
227
- 1. Multiple roles from different angles.
228
- 2. Parallel strategy.
229
- 3. guru + hacker + researcher + critic.
230
- 4. Do not save on models -- use the best.
231
-
232
- FORMAT (STRICT JSON):
233
- {"strategy": "parallel", "tasks": [...], "synthesis_prompt": "synthesize creative ideas"}""",
234
- description="Maximum roles, creative brainstorm",
235
- strategy="parallel",
236
- max_agents=5,
237
- cost_aware=False,
238
- auto_rank_by="coding"
239
- ),
240
- "economy": Conductor(
241
- name="economy",
242
- prompt="""You are Economy Conductor PinkSky. Solve task for minimum cost.
243
-
244
- RULES:
245
- 1. Start with TIER 4 (fast/cheap): nemotron-nano-9b, nemotron-nano-12b, nemotron-3-nano-30b.
246
- 2. Only if it fails -- escalate to TIER 3/2.
247
- 3. One role, one model.
248
-
249
- FORMAT (STRICT JSON):
250
- {"strategy": "single", "tasks": [{"role": "name", "model": "name", "prompt": "task"}], "synthesis_prompt": ""}""",
251
- description="Cheap models, budget saving",
252
- strategy="single",
253
- max_agents=1,
254
- cost_aware=True,
255
- auto_rank_by="speed"
256
- ),
257
- "review": Conductor(
258
- name="review",
259
- prompt="""You are Code Review Conductor PinkSky. Maximum quality code review.
260
-
261
- RULES:
262
- 1. techlead (architectural review) + critic (bugs/vulnerabilities) + guru (best practices).
263
- 2. Parallel review.
264
- 3. Synthesize into structured report.
265
-
266
- FORMAT (STRICT JSON):
267
- {"strategy": "parallel", "tasks": [{"role": "techlead", "model": "deepseek-v4-pro", "prompt": "architectural review"}, {"role": "critic", "model": "kimi-k2.6", "prompt": "bug hunting"}, {"role": "guru", "model": "mistral-large-3", "prompt": "best practices"}], "synthesis_prompt": "structured report with severity"}""",
268
- description="Focus on code review. Multi-angle code check.",
269
- strategy="parallel",
270
- max_agents=4,
271
- cost_aware=True,
272
- auto_rank_by="coding"
273
- ),
274
- "build": Conductor(
275
- name="build",
276
- prompt="""You are Project Build Conductor PinkSky. Build full project from spec.
277
-
278
- RULES:
279
- 1. Sequential: architect -> guru/hacker -> sdet -> critic.
280
- 2. Each stage -- separate call.
281
-
282
- FORMAT (STRICT JSON):
283
- {"strategy": "sequential", "tasks": [{"role": "architect", "model": "deepseek-v4-pro", "prompt": "architecture"}, {"role": "guru", "model": "kimi-k2.6", "prompt": "code"}, {"role": "sdet", "model": "mistral-medium-3.5", "prompt": "tests"}, {"role": "critic", "model": "gpt-oss-120b", "prompt": "audit"}], "synthesis_prompt": "assemble into single project"}""",
284
- description="Project build. Architecture -> code -> tests -> audit.",
285
- strategy="sequential",
286
- max_agents=5,
287
- cost_aware=True,
288
- auto_rank_by="coding"
289
- ),
290
- }
291
- if os.path.exists(CONDUCTORS_FILE):
292
- try:
293
- with open(CONDUCTORS_FILE, "r", encoding="utf-8") as f:
294
- custom = json.load(f)
295
- for k, v in custom.items():
296
- if k not in defaults:
297
- defaults[k] = Conductor(**v)
298
- except Exception as e:
299
- print(f"⚠️ Ошибка загрузки conductors.json: {e}")
300
- self.conductors = defaults
301
-
302
- def _load_history(self):
303
- if os.path.exists(HISTORY_FILE):
304
- try:
305
- with open(HISTORY_FILE, "r", encoding="utf-8") as f:
306
- data = json.load(f)
307
- self.chat_history = data.get("chat", [])
308
- self.skill_history = data.get("skill", [])
309
- self.build_history = data.get("build", [])
310
- except Exception as e:
311
- print(f"⚠️ Ошибка загрузки истории: {e}")
312
-
313
- def save_roles(self):
314
- data = {k: {"name": v.name, "prompt": v.prompt, "description": v.description,
315
- "preferred_models": v.preferred_models, "complexity": v.complexity, "tags": v.tags}
316
- for k, v in self.roles.items()}
317
- with open(ROLES_FILE, "w", encoding="utf-8") as f:
318
- json.dump(data, f, ensure_ascii=False, indent=2)
319
-
320
- def save_models(self):
321
- data = {k: {"name": v.name, "provider": v.provider, "endpoint": v.endpoint,
322
- "api_key_env": v.api_key_env, "context_window": v.context_window,
323
- "max_tokens": v.max_tokens, "cost_per_1k_input": v.cost_per_1k_input,
324
- "cost_per_1k_output": v.cost_per_1k_output,
325
- "coding_rank": v.coding_rank, "speed_rank": v.speed_rank, "reasoning_rank": v.reasoning_rank,
326
- "tags": v.tags}
327
- for k, v in self.models.items()}
328
- with open(MODELS_FILE, "w", encoding="utf-8") as f:
329
- json.dump(data, f, ensure_ascii=False, indent=2)
330
-
331
- def save_conductors(self):
332
- data = {k: {"name": v.name, "prompt": v.prompt, "description": v.description,
333
- "strategy": v.strategy, "max_agents": v.max_agents, "cost_aware": v.cost_aware,
334
- "auto_rank_by": v.auto_rank_by}
335
- for k, v in self.conductors.items()}
336
- with open(CONDUCTORS_FILE, "w", encoding="utf-8") as f:
337
- json.dump(data, f, ensure_ascii=False, indent=2)
338
-
339
- def save_history(self):
340
- data = {"chat": self.chat_history, "skill": self.skill_history, "build": self.build_history}
341
- with open(HISTORY_FILE, "w", encoding="utf-8") as f:
342
- json.dump(data, f, ensure_ascii=False, indent=2)
343
-
344
- def add_to_history(self, mode: str, role: str, content: str):
345
- entry = {"role": role, "content": content, "timestamp": datetime.now().isoformat()}
346
- if mode == "chat":
347
- self.chat_history.append(entry)
348
- elif mode == "skill":
349
- self.skill_history.append(entry)
350
- elif mode == "build":
351
- self.build_history.append(entry)
352
- self.save_history()
353
-
354
- def get_best_model(self, rank_by: str = "coding", min_tier: int = 1, max_tier: int = 5, exclude: List[str] = None) -> str:
355
- exclude = exclude or []
356
- candidates = []
357
- for name, model in self.models.items():
358
- if name in exclude or name == "hf_fallback":
359
- continue
360
- tier = 5
361
- if model.coding_rank <= 5: tier = 1
362
- elif model.coding_rank <= 12: tier = 2
363
- elif model.coding_rank <= 18: tier = 3
364
- elif model.coding_rank <= 24: tier = 4
365
- if min_tier <= tier <= max_tier:
366
- candidates.append((name, model))
367
- if not candidates:
368
- return "deepseek-v4-pro"
369
- if rank_by == "coding":
370
- candidates.sort(key=lambda x: x[1].coding_rank)
371
- elif rank_by == "speed":
372
- candidates.sort(key=lambda x: x[1].speed_rank)
373
- elif rank_by == "reasoning":
374
- candidates.sort(key=lambda x: x[1].reasoning_rank)
375
- elif rank_by == "balanced":
376
- candidates.sort(key=lambda x: (x[1].coding_rank + x[1].speed_rank + x[1].reasoning_rank) / 3)
377
- else:
378
- candidates.sort(key=lambda x: x[1].coding_rank)
379
- return candidates[0][0]
380
-
381
- def get_model_for_role(self, role_name: str, preference: str = None, rank_by: str = None) -> str:
382
- role = self.roles.get(role_name)
383
- if not role:
384
- return preference or self.current_model
385
- conductor = self.conductors.get(self.current_conductor, self.conductors["default"])
386
- rank_criteria = rank_by or conductor.auto_rank_by
387
- max_tier = 5
388
- if role.complexity == "high":
389
- max_tier = 2
390
- elif role.complexity == "medium":
391
- max_tier = 3
392
- if preference and preference in self.models:
393
- return preference
394
- available = [m for m in role.preferred_models if m in self.models and m != "hf_fallback"]
395
- if available:
396
- if conductor.cost_aware and rank_criteria != "coding":
397
- available.sort(key=lambda m: self.models[m].cost_per_1k_output)
398
- else:
399
- if rank_criteria == "coding":
400
- available.sort(key=lambda m: self.models[m].coding_rank)
401
- elif rank_criteria == "speed":
402
- available.sort(key=lambda m: self.models[m].speed_rank)
403
- elif rank_criteria == "reasoning":
404
- available.sort(key=lambda m: self.models[m].reasoning_rank)
405
- else:
406
- available.sort(key=lambda m: (self.models[m].coding_rank + self.models[m].speed_rank + self.models[m].reasoning_rank) / 3)
407
- return available[0]
408
- return self.get_best_model(rank_by=rank_criteria, max_tier=max_tier)
409
-
410
- def get_models_by_tier(self, tier: int) -> List[str]:
411
- result = []
412
- for name, model in self.models.items():
413
- if name == "hf_fallback":
414
- continue
415
- model_tier = 5
416
- if model.coding_rank <= 5: model_tier = 1
417
- elif model.coding_rank <= 12: model_tier = 2
418
- elif model.coding_rank <= 18: model_tier = 3
419
- elif model.coding_rank <= 24: model_tier = 4
420
- if model_tier == tier:
421
- result.append(name)
422
- return result
423
-
424
- def get_next_tier_model(self, current_model_name: str) -> Optional[str]:
425
- if current_model_name not in self.models:
426
- return None
427
- current = self.models[current_model_name]
428
- current_tier = 5
429
- if current.coding_rank <= 5: current_tier = 1
430
- elif current.coding_rank <= 12: current_tier = 2
431
- elif current.coding_rank <= 18: current_tier = 3
432
- elif current.coding_rank <= 24: current_tier = 4
433
- next_tier = current_tier + 1
434
- if next_tier > 5:
435
- return None
436
- models_in_tier = self.get_models_by_tier(next_tier)
437
- if models_in_tier:
438
- return models_in_tier[0]
439
- return None
440
-
441
- def export_history_json(self) -> str:
442
- return json.dumps({"exported_at": datetime.now().isoformat(), "chat": self.chat_history, "skill": self.skill_history, "build": self.build_history}, ensure_ascii=False, indent=2)
443
-
444
- def export_history_md(self) -> str:
445
- lines = ["# PinkSky History Export", ""]
446
- lines.append("*Exported: " + datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "*")
447
- lines.append("")
448
- for mode, history in [("Chat", self.chat_history), ("Skill", self.skill_history), ("Build", self.build_history)]:
449
- lines.append("## " + mode + " Mode")
450
- lines.append("")
451
- for entry in history:
452
- ts = entry.get("timestamp", "unknown")
453
- role = entry.get("role", "unknown")
454
- content = entry.get("content", "")
455
- lines.append("### " + role + " (" + ts + ")")
456
- lines.append("")
457
- lines.append("```")
458
- lines.append(content[:500])
459
- lines.append("```")
460
- lines.append("")
461
- return "\n".join(lines)
462
-
463
- STATE = PinkSkyState()