claude-code-workflow 7.2.29 → 7.2.30

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 (124) hide show
  1. package/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json +2 -2
  2. package/.ccw/workflows/cli-templates/schemas/task-schema.json +14 -7
  3. package/.claude/agents/action-planning-agent.md +7 -4
  4. package/.claude/agents/cli-explore-agent.md +77 -63
  5. package/.claude/agents/cli-lite-planning-agent.md +11 -10
  6. package/.claude/agents/issue-plan-agent.md +421 -426
  7. package/.claude/commands/workflow/spec/setup.md +1 -1
  8. package/.claude/skills/ccw-chain/SKILL.md +119 -0
  9. package/.claude/skills/ccw-chain/chains/ccw-cycle.json +21 -0
  10. package/.claude/skills/ccw-chain/chains/ccw-exploration.json +47 -0
  11. package/.claude/skills/ccw-chain/chains/ccw-issue.json +33 -0
  12. package/.claude/skills/ccw-chain/chains/ccw-lightweight.json +57 -0
  13. package/.claude/skills/ccw-chain/chains/ccw-main.json +52 -0
  14. package/.claude/skills/ccw-chain/chains/ccw-standard.json +39 -0
  15. package/.claude/skills/ccw-chain/chains/ccw-team.json +10 -0
  16. package/.claude/skills/ccw-chain/chains/ccw-with-file.json +31 -0
  17. package/.claude/skills/ccw-chain/phases/analyze-with-file.md +788 -0
  18. package/.claude/skills/ccw-chain/phases/brainstorm/SKILL.md +408 -0
  19. package/.claude/skills/ccw-chain/phases/brainstorm/phases/01-mode-routing.md +207 -0
  20. package/.claude/skills/ccw-chain/phases/brainstorm/phases/02-artifacts.md +567 -0
  21. package/.claude/skills/ccw-chain/phases/brainstorm/phases/03-role-analysis.md +748 -0
  22. package/.claude/skills/ccw-chain/phases/brainstorm/phases/04-synthesis.md +827 -0
  23. package/.claude/skills/ccw-chain/phases/brainstorm-with-file.md +482 -0
  24. package/.claude/skills/ccw-chain/phases/collaborative-plan-with-file.md +639 -0
  25. package/.claude/skills/ccw-chain/phases/debug-with-file.md +656 -0
  26. package/.claude/skills/ccw-chain/phases/integration-test-cycle.md +936 -0
  27. package/.claude/skills/ccw-chain/phases/issue-convert-to-plan.md +720 -0
  28. package/.claude/skills/ccw-chain/phases/issue-discover.md +483 -0
  29. package/.claude/skills/ccw-chain/phases/issue-execute.md +629 -0
  30. package/.claude/skills/ccw-chain/phases/issue-from-brainstorm.md +382 -0
  31. package/.claude/skills/ccw-chain/phases/issue-plan.md +343 -0
  32. package/.claude/skills/ccw-chain/phases/issue-queue.md +464 -0
  33. package/.claude/skills/ccw-chain/phases/refactor-cycle.md +852 -0
  34. package/.claude/skills/ccw-chain/phases/review-cycle/SKILL.md +132 -0
  35. package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-fix.md +760 -0
  36. package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-module.md +764 -0
  37. package/.claude/skills/ccw-chain/phases/review-cycle/phases/review-session.md +775 -0
  38. package/.claude/skills/ccw-chain/phases/roadmap-with-file.md +544 -0
  39. package/.claude/skills/ccw-chain/phases/spec-generator/SKILL.md +338 -0
  40. package/.claude/skills/ccw-chain/phases/spec-generator/phases/01-5-requirement-clarification.md +404 -0
  41. package/.claude/skills/ccw-chain/phases/spec-generator/phases/01-discovery.md +257 -0
  42. package/.claude/skills/ccw-chain/phases/spec-generator/phases/02-product-brief.md +274 -0
  43. package/.claude/skills/ccw-chain/phases/spec-generator/phases/03-requirements.md +184 -0
  44. package/.claude/skills/ccw-chain/phases/spec-generator/phases/04-architecture.md +248 -0
  45. package/.claude/skills/ccw-chain/phases/spec-generator/phases/05-epics-stories.md +178 -0
  46. package/.claude/skills/ccw-chain/phases/spec-generator/phases/06-5-auto-fix.md +144 -0
  47. package/.claude/skills/ccw-chain/phases/spec-generator/phases/06-readiness-check.md +480 -0
  48. package/.claude/skills/ccw-chain/phases/team-planex.md +123 -0
  49. package/.claude/skills/ccw-chain/phases/ui-design-explore-auto.md +678 -0
  50. package/.claude/skills/ccw-chain/phases/unified-execute-with-file.md +870 -0
  51. package/.claude/skills/ccw-chain/phases/workflow-execute/SKILL.md +625 -0
  52. package/.claude/skills/ccw-chain/phases/workflow-execute/phases/06-review.md +215 -0
  53. package/.claude/skills/ccw-chain/phases/workflow-lite-plan.md +616 -0
  54. package/.claude/skills/ccw-chain/phases/workflow-multi-cli-plan.md +424 -0
  55. package/.claude/skills/ccw-chain/phases/workflow-plan/SKILL.md +466 -0
  56. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/01-session-discovery.md +99 -0
  57. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/02-context-gathering.md +338 -0
  58. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/03-conflict-resolution.md +422 -0
  59. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/04-task-generation.md +440 -0
  60. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/05-plan-verify.md +395 -0
  61. package/.claude/skills/ccw-chain/phases/workflow-plan/phases/06-replan.md +594 -0
  62. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/SKILL.md +527 -0
  63. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/01-session-discovery.md +57 -0
  64. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/02-context-gathering.md +407 -0
  65. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/03-test-coverage-analysis.md +172 -0
  66. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/04-conflict-resolution.md +426 -0
  67. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/05-tdd-task-generation.md +473 -0
  68. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/06-tdd-structure-validation.md +189 -0
  69. package/.claude/skills/ccw-chain/phases/workflow-tdd-plan/phases/07-tdd-verify.md +635 -0
  70. package/.claude/skills/ccw-chain/phases/workflow-test-fix/SKILL.md +482 -0
  71. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/01-session-start.md +60 -0
  72. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/02-test-context-gather.md +493 -0
  73. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/03-test-concept-enhanced.md +150 -0
  74. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/04-test-task-generate.md +346 -0
  75. package/.claude/skills/ccw-chain/phases/workflow-test-fix/phases/05-test-cycle-execute.md +538 -0
  76. package/.claude/skills/ccw-chain/specs/auto-mode.md +47 -0
  77. package/.claude/skills/ccw-chain/specs/intent-patterns.md +60 -0
  78. package/.claude/skills/chain-loader/SKILL.md +78 -0
  79. package/.claude/skills/chain-loader/phases/01-analyze-skill.md +53 -0
  80. package/.claude/skills/chain-loader/phases/02-design-graph.md +73 -0
  81. package/.claude/skills/chain-loader/phases/03-generate-validate.md +75 -0
  82. package/.claude/skills/chain-loader/specs/chain-schema.md +99 -0
  83. package/.claude/skills/chain-loader/specs/design-patterns.md +99 -0
  84. package/.claude/skills/chain-loader/templates/chain-json.md +63 -0
  85. package/.claude/skills/review-cycle/phases/review-module.md +764 -764
  86. package/.claude/skills/review-cycle/phases/review-session.md +775 -775
  87. package/.claude/skills/workflow-multi-cli-plan/SKILL.md +2 -2
  88. package/.claude/skills/workflow-plan/phases/03-conflict-resolution.md +422 -422
  89. package/.claude/skills/workflow-plan/phases/05-plan-verify.md +395 -395
  90. package/.claude/skills/workflow-tdd-plan/phases/02-context-gathering.md +407 -407
  91. package/.claude/skills/workflow-tdd-plan/phases/04-conflict-resolution.md +426 -426
  92. package/.claude/skills/workflow-test-fix/phases/02-test-context-gather.md +493 -493
  93. package/README.md +14 -0
  94. package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -1
  95. package/ccw/dist/core/routes/litellm-api-routes.js +0 -23
  96. package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -1
  97. package/ccw/dist/tools/chain-loader.d.ts +10 -0
  98. package/ccw/dist/tools/chain-loader.d.ts.map +1 -0
  99. package/ccw/dist/tools/chain-loader.js +642 -0
  100. package/ccw/dist/tools/chain-loader.js.map +1 -0
  101. package/ccw/dist/tools/index.d.ts.map +1 -1
  102. package/ccw/dist/tools/index.js +2 -0
  103. package/ccw/dist/tools/index.js.map +1 -1
  104. package/ccw/dist/tools/json-builder.js +20 -0
  105. package/ccw/dist/tools/json-builder.js.map +1 -1
  106. package/ccw/dist/types/chain-types.d.ts +72 -0
  107. package/ccw/dist/types/chain-types.d.ts.map +1 -0
  108. package/ccw/dist/types/chain-types.js +5 -0
  109. package/ccw/dist/types/chain-types.js.map +1 -0
  110. package/ccw/scripts/prepublish-clean.mjs +0 -1
  111. package/package.json +1 -3
  112. package/ccw-litellm/README.md +0 -180
  113. package/ccw-litellm/pyproject.toml +0 -35
  114. package/ccw-litellm/src/ccw_litellm/__init__.py +0 -47
  115. package/ccw-litellm/src/ccw_litellm/cli.py +0 -108
  116. package/ccw-litellm/src/ccw_litellm/clients/__init__.py +0 -12
  117. package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +0 -270
  118. package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +0 -198
  119. package/ccw-litellm/src/ccw_litellm/config/__init__.py +0 -22
  120. package/ccw-litellm/src/ccw_litellm/config/loader.py +0 -343
  121. package/ccw-litellm/src/ccw_litellm/config/models.py +0 -162
  122. package/ccw-litellm/src/ccw_litellm/interfaces/__init__.py +0 -14
  123. package/ccw-litellm/src/ccw_litellm/interfaces/embedder.py +0 -52
  124. package/ccw-litellm/src/ccw_litellm/interfaces/llm.py +0 -45
@@ -1,343 +0,0 @@
1
- """Configuration loader with environment variable substitution."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import os
7
- import re
8
- from pathlib import Path
9
- from typing import Any
10
-
11
- import yaml
12
-
13
- from .models import LiteLLMConfig
14
-
15
- # Default configuration paths
16
- # JSON format (UI config) takes priority over YAML format
17
- DEFAULT_JSON_CONFIG_PATH = Path.home() / ".ccw" / "config" / "litellm-api-config.json"
18
- DEFAULT_YAML_CONFIG_PATH = Path.home() / ".ccw" / "config" / "litellm-config.yaml"
19
- # Keep backward compatibility
20
- DEFAULT_CONFIG_PATH = DEFAULT_YAML_CONFIG_PATH
21
-
22
- # Global configuration singleton
23
- _config_instance: LiteLLMConfig | None = None
24
-
25
-
26
- def _substitute_env_vars(value: Any) -> Any:
27
- """Recursively substitute environment variables in configuration values.
28
-
29
- Supports ${ENV_VAR} and ${ENV_VAR:-default} syntax.
30
-
31
- Args:
32
- value: Configuration value (str, dict, list, or primitive)
33
-
34
- Returns:
35
- Value with environment variables substituted
36
- """
37
- if isinstance(value, str):
38
- # Pattern: ${VAR} or ${VAR:-default}
39
- pattern = r"\$\{([^:}]+)(?::-(.*?))?\}"
40
-
41
- def replace_var(match: re.Match) -> str:
42
- var_name = match.group(1)
43
- default_value = match.group(2) if match.group(2) is not None else ""
44
- return os.environ.get(var_name, default_value)
45
-
46
- return re.sub(pattern, replace_var, value)
47
-
48
- if isinstance(value, dict):
49
- return {k: _substitute_env_vars(v) for k, v in value.items()}
50
-
51
- if isinstance(value, list):
52
- return [_substitute_env_vars(item) for item in value]
53
-
54
- return value
55
-
56
-
57
- def _get_default_config() -> dict[str, Any]:
58
- """Get default configuration when no config file exists.
59
-
60
- Returns:
61
- Default configuration dictionary
62
- """
63
- return {
64
- "version": 1,
65
- "default_provider": "openai",
66
- "providers": {
67
- "openai": {
68
- "api_key": "${OPENAI_API_KEY}",
69
- "api_base": "https://api.openai.com/v1",
70
- },
71
- },
72
- "llm_models": {
73
- "default": {
74
- "provider": "openai",
75
- "model": "gpt-4",
76
- },
77
- "fast": {
78
- "provider": "openai",
79
- "model": "gpt-3.5-turbo",
80
- },
81
- },
82
- "embedding_models": {
83
- "default": {
84
- "provider": "openai",
85
- "model": "text-embedding-3-small",
86
- "dimensions": 1536,
87
- },
88
- },
89
- }
90
-
91
-
92
- def _convert_json_to_internal_format(json_config: dict[str, Any]) -> dict[str, Any]:
93
- """Convert UI JSON config format to internal format.
94
-
95
- The UI stores config in a different structure:
96
- - providers: array of {id, name, type, apiKey, apiBase, llmModels[], embeddingModels[]}
97
-
98
- Internal format uses:
99
- - providers: dict of {provider_id: {api_key, api_base}}
100
- - llm_models: dict of {model_id: {provider, model}}
101
- - embedding_models: dict of {model_id: {provider, model, dimensions}}
102
-
103
- Args:
104
- json_config: Configuration in UI JSON format
105
-
106
- Returns:
107
- Configuration in internal format
108
- """
109
- providers: dict[str, Any] = {}
110
- llm_models: dict[str, Any] = {}
111
- embedding_models: dict[str, Any] = {}
112
- reranker_models: dict[str, Any] = {}
113
- default_provider: str | None = None
114
-
115
- for provider in json_config.get("providers", []):
116
- if not provider.get("enabled", True):
117
- continue
118
-
119
- provider_id = provider.get("id", "")
120
- if not provider_id:
121
- continue
122
-
123
- # Set first enabled provider as default
124
- if default_provider is None:
125
- default_provider = provider_id
126
-
127
- # Convert provider with advanced settings
128
- provider_config: dict[str, Any] = {
129
- "api_key": provider.get("apiKey", ""),
130
- "api_base": provider.get("apiBase"),
131
- }
132
-
133
- # Map advanced settings
134
- adv = provider.get("advancedSettings", {})
135
- if adv.get("timeout"):
136
- provider_config["timeout"] = adv["timeout"]
137
- if adv.get("maxRetries"):
138
- provider_config["max_retries"] = adv["maxRetries"]
139
- if adv.get("organization"):
140
- provider_config["organization"] = adv["organization"]
141
- if adv.get("apiVersion"):
142
- provider_config["api_version"] = adv["apiVersion"]
143
- if adv.get("customHeaders"):
144
- provider_config["custom_headers"] = adv["customHeaders"]
145
-
146
- providers[provider_id] = provider_config
147
-
148
- # Convert LLM models
149
- for model in provider.get("llmModels", []):
150
- if not model.get("enabled", True):
151
- continue
152
- model_id = model.get("id", "")
153
- if not model_id:
154
- continue
155
-
156
- llm_model_config: dict[str, Any] = {
157
- "provider": provider_id,
158
- "model": model.get("name", ""),
159
- }
160
- # Add model-specific endpoint settings
161
- endpoint = model.get("endpointSettings", {})
162
- if endpoint.get("baseUrl"):
163
- llm_model_config["api_base"] = endpoint["baseUrl"]
164
- if endpoint.get("timeout"):
165
- llm_model_config["timeout"] = endpoint["timeout"]
166
- if endpoint.get("maxRetries"):
167
- llm_model_config["max_retries"] = endpoint["maxRetries"]
168
-
169
- # Add capabilities
170
- caps = model.get("capabilities", {})
171
- if caps.get("contextWindow"):
172
- llm_model_config["context_window"] = caps["contextWindow"]
173
- if caps.get("maxOutputTokens"):
174
- llm_model_config["max_output_tokens"] = caps["maxOutputTokens"]
175
-
176
- llm_models[model_id] = llm_model_config
177
-
178
- # Convert embedding models
179
- for model in provider.get("embeddingModels", []):
180
- if not model.get("enabled", True):
181
- continue
182
- model_id = model.get("id", "")
183
- if not model_id:
184
- continue
185
-
186
- embedding_model_config: dict[str, Any] = {
187
- "provider": provider_id,
188
- "model": model.get("name", ""),
189
- "dimensions": model.get("capabilities", {}).get("embeddingDimension", 1536),
190
- "max_input_tokens": model.get("capabilities", {}).get("maxInputTokens", 8192),
191
- }
192
- # Add model-specific endpoint settings
193
- endpoint = model.get("endpointSettings", {})
194
- if endpoint.get("baseUrl"):
195
- embedding_model_config["api_base"] = endpoint["baseUrl"]
196
- if endpoint.get("timeout"):
197
- embedding_model_config["timeout"] = endpoint["timeout"]
198
-
199
- embedding_models[model_id] = embedding_model_config
200
-
201
- # Convert reranker models
202
- for model in provider.get("rerankerModels", []):
203
- if not model.get("enabled", True):
204
- continue
205
- model_id = model.get("id", "")
206
- if not model_id:
207
- continue
208
-
209
- reranker_model_config: dict[str, Any] = {
210
- "provider": provider_id,
211
- "model": model.get("name", ""),
212
- "max_input_tokens": model.get("capabilities", {}).get("maxInputTokens", 8192),
213
- "top_k": model.get("capabilities", {}).get("topK", 50),
214
- }
215
- # Add model-specific endpoint settings
216
- endpoint = model.get("endpointSettings", {})
217
- if endpoint.get("baseUrl"):
218
- reranker_model_config["api_base"] = endpoint["baseUrl"]
219
- if endpoint.get("timeout"):
220
- reranker_model_config["timeout"] = endpoint["timeout"]
221
-
222
- reranker_models[model_id] = reranker_model_config
223
-
224
- # Ensure we have defaults if no models found
225
- if not llm_models:
226
- llm_models["default"] = {
227
- "provider": default_provider or "openai",
228
- "model": "gpt-4",
229
- }
230
-
231
- if not embedding_models:
232
- embedding_models["default"] = {
233
- "provider": default_provider or "openai",
234
- "model": "text-embedding-3-small",
235
- "dimensions": 1536,
236
- "max_input_tokens": 8191,
237
- }
238
-
239
- return {
240
- "version": json_config.get("version", 1),
241
- "default_provider": default_provider or "openai",
242
- "providers": providers,
243
- "llm_models": llm_models,
244
- "embedding_models": embedding_models,
245
- "reranker_models": reranker_models,
246
- }
247
-
248
-
249
- def load_config(config_path: Path | str | None = None) -> LiteLLMConfig:
250
- """Load LiteLLM configuration from JSON or YAML file.
251
-
252
- Priority order:
253
- 1. Explicit config_path if provided
254
- 2. JSON config (UI format): ~/.ccw/config/litellm-api-config.json
255
- 3. YAML config: ~/.ccw/config/litellm-config.yaml
256
- 4. Default configuration
257
-
258
- Args:
259
- config_path: Path to configuration file (optional)
260
-
261
- Returns:
262
- Parsed and validated configuration
263
-
264
- Raises:
265
- FileNotFoundError: If config file not found and no default available
266
- ValueError: If configuration is invalid
267
- """
268
- raw_config: dict[str, Any] | None = None
269
- is_json_format = False
270
-
271
- if config_path is not None:
272
- config_path = Path(config_path)
273
- if config_path.exists():
274
- try:
275
- with open(config_path, "r", encoding="utf-8") as f:
276
- if config_path.suffix == ".json":
277
- raw_config = json.load(f)
278
- is_json_format = True
279
- else:
280
- raw_config = yaml.safe_load(f)
281
- except Exception as e:
282
- raise ValueError(f"Failed to load configuration from {config_path}: {e}") from e
283
-
284
- # Check JSON config first (UI format)
285
- if raw_config is None and DEFAULT_JSON_CONFIG_PATH.exists():
286
- try:
287
- with open(DEFAULT_JSON_CONFIG_PATH, "r", encoding="utf-8") as f:
288
- raw_config = json.load(f)
289
- is_json_format = True
290
- except Exception:
291
- pass # Fall through to YAML
292
-
293
- # Check YAML config
294
- if raw_config is None and DEFAULT_YAML_CONFIG_PATH.exists():
295
- try:
296
- with open(DEFAULT_YAML_CONFIG_PATH, "r", encoding="utf-8") as f:
297
- raw_config = yaml.safe_load(f)
298
- except Exception:
299
- pass # Fall through to default
300
-
301
- # Use default configuration
302
- if raw_config is None:
303
- raw_config = _get_default_config()
304
-
305
- # Convert JSON format to internal format if needed
306
- if is_json_format:
307
- raw_config = _convert_json_to_internal_format(raw_config)
308
-
309
- # Substitute environment variables
310
- config_data = _substitute_env_vars(raw_config)
311
-
312
- # Validate and parse with Pydantic
313
- try:
314
- return LiteLLMConfig.model_validate(config_data)
315
- except Exception as e:
316
- raise ValueError(f"Invalid configuration: {e}") from e
317
-
318
-
319
- def get_config(config_path: Path | str | None = None, reload: bool = False) -> LiteLLMConfig:
320
- """Get global configuration singleton.
321
-
322
- Args:
323
- config_path: Path to configuration file (default: ~/.ccw/config/litellm-config.yaml)
324
- reload: Force reload configuration from disk
325
-
326
- Returns:
327
- Global configuration instance
328
- """
329
- global _config_instance
330
-
331
- if _config_instance is None or reload:
332
- _config_instance = load_config(config_path)
333
-
334
- return _config_instance
335
-
336
-
337
- def reset_config() -> None:
338
- """Reset global configuration singleton.
339
-
340
- Useful for testing.
341
- """
342
- global _config_instance
343
- _config_instance = None
@@ -1,162 +0,0 @@
1
- """Pydantic configuration models for LiteLLM integration."""
2
-
3
- from __future__ import annotations
4
-
5
- from typing import Any
6
-
7
- from pydantic import BaseModel, Field
8
-
9
-
10
- class ProviderConfig(BaseModel):
11
- """Provider API configuration.
12
-
13
- Supports environment variable substitution in the format ${ENV_VAR}.
14
- """
15
-
16
- api_key: str | None = None
17
- api_base: str | None = None
18
-
19
- model_config = {"extra": "allow"}
20
-
21
-
22
- class LLMModelConfig(BaseModel):
23
- """LLM model configuration."""
24
-
25
- provider: str
26
- model: str
27
-
28
- model_config = {"extra": "allow"}
29
-
30
-
31
- class EmbeddingModelConfig(BaseModel):
32
- """Embedding model configuration."""
33
-
34
- provider: str # "openai", "fastembed", "ollama", etc.
35
- model: str
36
- dimensions: int
37
- max_input_tokens: int = 8192 # Maximum tokens per embedding request
38
-
39
- model_config = {"extra": "allow"}
40
-
41
-
42
- class RerankerModelConfig(BaseModel):
43
- """Reranker model configuration."""
44
-
45
- provider: str # "siliconflow", "cohere", "jina", etc.
46
- model: str
47
- max_input_tokens: int = 8192 # Maximum tokens per reranking request
48
- top_k: int = 50 # Default top_k for reranking
49
-
50
- model_config = {"extra": "allow"}
51
-
52
-
53
- class LiteLLMConfig(BaseModel):
54
- """Root configuration for LiteLLM integration.
55
-
56
- Example YAML:
57
- version: 1
58
- default_provider: openai
59
- providers:
60
- openai:
61
- api_key: ${OPENAI_API_KEY}
62
- api_base: https://api.openai.com/v1
63
- anthropic:
64
- api_key: ${ANTHROPIC_API_KEY}
65
- llm_models:
66
- default:
67
- provider: openai
68
- model: gpt-4
69
- fast:
70
- provider: openai
71
- model: gpt-3.5-turbo
72
- embedding_models:
73
- default:
74
- provider: openai
75
- model: text-embedding-3-small
76
- dimensions: 1536
77
- """
78
-
79
- version: int = 1
80
- default_provider: str = "openai"
81
- providers: dict[str, ProviderConfig] = Field(default_factory=dict)
82
- llm_models: dict[str, LLMModelConfig] = Field(default_factory=dict)
83
- embedding_models: dict[str, EmbeddingModelConfig] = Field(default_factory=dict)
84
- reranker_models: dict[str, RerankerModelConfig] = Field(default_factory=dict)
85
-
86
- model_config = {"extra": "allow"}
87
-
88
- def get_llm_model(self, model: str = "default") -> LLMModelConfig:
89
- """Get LLM model configuration by name.
90
-
91
- Args:
92
- model: Model name or "default"
93
-
94
- Returns:
95
- LLM model configuration
96
-
97
- Raises:
98
- ValueError: If model not found
99
- """
100
- if model not in self.llm_models:
101
- raise ValueError(
102
- f"LLM model '{model}' not found in configuration. "
103
- f"Available models: {list(self.llm_models.keys())}"
104
- )
105
- return self.llm_models[model]
106
-
107
- def get_embedding_model(self, model: str = "default") -> EmbeddingModelConfig:
108
- """Get embedding model configuration by name.
109
-
110
- Args:
111
- model: Model name or "default"
112
-
113
- Returns:
114
- Embedding model configuration
115
-
116
- Raises:
117
- ValueError: If model not found
118
- """
119
- if model not in self.embedding_models:
120
- raise ValueError(
121
- f"Embedding model '{model}' not found in configuration. "
122
- f"Available models: {list(self.embedding_models.keys())}"
123
- )
124
- return self.embedding_models[model]
125
-
126
- def get_reranker_model(self, model: str = "default") -> RerankerModelConfig:
127
- """Get reranker model configuration by name.
128
-
129
- Args:
130
- model: Model name or "default"
131
-
132
- Returns:
133
- Reranker model configuration
134
-
135
- Raises:
136
- ValueError: If model not found
137
- """
138
- if model not in self.reranker_models:
139
- raise ValueError(
140
- f"Reranker model '{model}' not found in configuration. "
141
- f"Available models: {list(self.reranker_models.keys())}"
142
- )
143
- return self.reranker_models[model]
144
-
145
- def get_provider(self, provider: str) -> ProviderConfig:
146
- """Get provider configuration by name.
147
-
148
- Args:
149
- provider: Provider name
150
-
151
- Returns:
152
- Provider configuration
153
-
154
- Raises:
155
- ValueError: If provider not found
156
- """
157
- if provider not in self.providers:
158
- raise ValueError(
159
- f"Provider '{provider}' not found in configuration. "
160
- f"Available providers: {list(self.providers.keys())}"
161
- )
162
- return self.providers[provider]
@@ -1,14 +0,0 @@
1
- """Abstract interfaces for ccw-litellm."""
2
-
3
- from __future__ import annotations
4
-
5
- from .embedder import AbstractEmbedder
6
- from .llm import AbstractLLMClient, ChatMessage, LLMResponse
7
-
8
- __all__ = [
9
- "AbstractEmbedder",
10
- "AbstractLLMClient",
11
- "ChatMessage",
12
- "LLMResponse",
13
- ]
14
-
@@ -1,52 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- from abc import ABC, abstractmethod
5
- from typing import Any, Sequence
6
-
7
- import numpy as np
8
- from numpy.typing import NDArray
9
-
10
-
11
- class AbstractEmbedder(ABC):
12
- """Embedding interface compatible with fastembed-style embedders.
13
-
14
- Implementers only need to provide the synchronous `embed` method; an
15
- asynchronous `aembed` wrapper is provided for convenience.
16
- """
17
-
18
- @property
19
- @abstractmethod
20
- def dimensions(self) -> int:
21
- """Embedding vector size."""
22
-
23
- @abstractmethod
24
- def embed(
25
- self,
26
- texts: str | Sequence[str],
27
- *,
28
- batch_size: int | None = None,
29
- **kwargs: Any,
30
- ) -> NDArray[np.floating]:
31
- """Embed one or more texts.
32
-
33
- Returns:
34
- A numpy array of shape (n_texts, dimensions).
35
- """
36
-
37
- async def aembed(
38
- self,
39
- texts: str | Sequence[str],
40
- *,
41
- batch_size: int | None = None,
42
- **kwargs: Any,
43
- ) -> NDArray[np.floating]:
44
- """Async wrapper around `embed` using a worker thread by default."""
45
-
46
- return await asyncio.to_thread(
47
- self.embed,
48
- texts,
49
- batch_size=batch_size,
50
- **kwargs,
51
- )
52
-
@@ -1,45 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- from abc import ABC, abstractmethod
5
- from dataclasses import dataclass
6
- from typing import Any, Literal, Sequence
7
-
8
-
9
- @dataclass(frozen=True, slots=True)
10
- class ChatMessage:
11
- role: Literal["system", "user", "assistant", "tool"]
12
- content: str
13
-
14
-
15
- @dataclass(frozen=True, slots=True)
16
- class LLMResponse:
17
- content: str
18
- raw: Any | None = None
19
-
20
-
21
- class AbstractLLMClient(ABC):
22
- """LiteLLM-like client interface.
23
-
24
- Implementers only need to provide synchronous methods; async wrappers are
25
- provided via `asyncio.to_thread`.
26
- """
27
-
28
- @abstractmethod
29
- def chat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> LLMResponse:
30
- """Chat completion for a sequence of messages."""
31
-
32
- @abstractmethod
33
- def complete(self, prompt: str, **kwargs: Any) -> LLMResponse:
34
- """Text completion for a prompt."""
35
-
36
- async def achat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> LLMResponse:
37
- """Async wrapper around `chat` using a worker thread by default."""
38
-
39
- return await asyncio.to_thread(self.chat, messages, **kwargs)
40
-
41
- async def acomplete(self, prompt: str, **kwargs: Any) -> LLMResponse:
42
- """Async wrapper around `complete` using a worker thread by default."""
43
-
44
- return await asyncio.to_thread(self.complete, prompt, **kwargs)
45
-