FreshPixels commited on
Commit
a660ade
·
verified ·
1 Parent(s): b52600c

Delete server/state.py

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