claude-code-workflow 7.2.29 → 7.3.0

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 (169) 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/commands/workflow-skill.md +130 -0
  9. package/.claude/skills/ccw-chain/SKILL.md +92 -0
  10. package/.claude/skills/ccw-chain/chains/ccw-cycle.json +31 -0
  11. package/.claude/skills/ccw-chain/chains/ccw-exploration.json +58 -0
  12. package/.claude/skills/ccw-chain/chains/ccw-issue.json +44 -0
  13. package/.claude/skills/ccw-chain/chains/ccw-lightweight.json +71 -0
  14. package/.claude/skills/ccw-chain/chains/ccw-main.json +65 -0
  15. package/.claude/skills/ccw-chain/chains/ccw-standard.json +51 -0
  16. package/.claude/skills/ccw-chain/chains/ccw-team.json +15 -0
  17. package/.claude/skills/ccw-chain/chains/ccw-with-file.json +47 -0
  18. package/.claude/skills/ccw-chain/specs/auto-mode.md +47 -0
  19. package/.claude/skills/chain-loader/SKILL.md +78 -0
  20. package/.claude/skills/chain-loader/phases/01-analyze-skill.md +53 -0
  21. package/.claude/skills/chain-loader/phases/02-design-graph.md +73 -0
  22. package/.claude/skills/chain-loader/phases/03-generate-validate.md +75 -0
  23. package/.claude/skills/chain-loader/specs/chain-schema.md +126 -0
  24. package/.claude/skills/chain-loader/specs/design-patterns.md +99 -0
  25. package/.claude/skills/chain-loader/templates/chain-json.md +63 -0
  26. package/.claude/skills/review-cycle/phases/review-module.md +764 -764
  27. package/.claude/skills/review-cycle/phases/review-session.md +775 -775
  28. package/.claude/skills/workflow-multi-cli-plan/SKILL.md +2 -2
  29. package/.claude/skills/workflow-plan/SKILL.md +1 -0
  30. package/.claude/skills/workflow-plan/phases/01-session-discovery.md +19 -2
  31. package/.claude/skills/workflow-plan/phases/02-context-gathering.md +2 -2
  32. package/.claude/skills/workflow-plan/phases/03-conflict-resolution.md +422 -422
  33. package/.claude/skills/workflow-plan/phases/04-task-generation.md +9 -1
  34. package/.claude/skills/workflow-plan/phases/05-plan-verify.md +395 -395
  35. package/.claude/skills/workflow-tdd-plan/phases/02-context-gathering.md +407 -407
  36. package/.claude/skills/workflow-tdd-plan/phases/04-conflict-resolution.md +426 -426
  37. package/.claude/skills/workflow-test-fix/phases/02-test-context-gather.md +493 -493
  38. package/.codex/skills/analyze-with-file/SKILL.md +383 -134
  39. package/.codex/skills/brainstorm/SKILL.md +3 -3
  40. package/.codex/skills/brainstorm-with-file/SKILL.md +208 -88
  41. package/.codex/skills/clean/SKILL.md +1 -1
  42. package/.codex/skills/csv-wave-pipeline/SKILL.md +2 -2
  43. package/.codex/skills/investigate/orchestrator.md +24 -0
  44. package/.codex/skills/issue-discover/SKILL.md +374 -361
  45. package/.codex/skills/issue-discover/phases/01-issue-new.md +1 -1
  46. package/.codex/skills/issue-discover/phases/02-discover.md +2 -2
  47. package/.codex/skills/issue-discover/phases/03-discover-by-prompt.md +1 -1
  48. package/.codex/skills/issue-discover/phases/04-quick-execute.md +2 -2
  49. package/.codex/skills/parallel-dev-cycle/SKILL.md +44 -37
  50. package/.codex/skills/project-documentation-workflow/SKILL.md +1 -1
  51. package/.codex/skills/review-cycle/SKILL.md +31 -12
  52. package/.codex/skills/roadmap-with-file/SKILL.md +141 -133
  53. package/.codex/skills/security-audit/orchestrator.md +29 -0
  54. package/.codex/skills/session-sync/SKILL.md +1 -1
  55. package/.codex/skills/ship/orchestrator.md +24 -0
  56. package/.codex/skills/spec-add/SKILL.md +5 -5
  57. package/.codex/skills/spec-generator/SKILL.md +33 -2
  58. package/.codex/skills/spec-generator/phases/01-5-requirement-clarification.md +3 -3
  59. package/.codex/skills/spec-generator/phases/01-discovery.md +1 -1
  60. package/.codex/skills/spec-generator/phases/02-product-brief.md +1 -1
  61. package/.codex/skills/spec-generator/phases/03-requirements.md +1 -1
  62. package/.codex/skills/spec-generator/phases/04-architecture.md +1 -1
  63. package/.codex/skills/spec-generator/phases/05-epics-stories.md +1 -1
  64. package/.codex/skills/spec-generator/phases/06-readiness-check.md +1 -1
  65. package/.codex/skills/spec-generator/phases/07-issue-export.md +1 -1
  66. package/.codex/skills/spec-setup/SKILL.md +669 -669
  67. package/.codex/skills/team-arch-opt/specs/team-config.json +1 -1
  68. package/.codex/skills/team-brainstorm/SKILL.md +259 -259
  69. package/.codex/skills/team-coordinate/SKILL.md +359 -359
  70. package/.codex/skills/team-coordinate/roles/coordinator/commands/monitor.md +1 -1
  71. package/.codex/skills/team-designer/SKILL.md +27 -1
  72. package/.codex/skills/team-designer/phases/01-requirements-analysis.md +2 -2
  73. package/.codex/skills/team-designer/phases/02-scaffold-generation.md +1 -1
  74. package/.codex/skills/team-designer/phases/04-validation.md +1 -1
  75. package/.codex/skills/team-executor/SKILL.md +218 -218
  76. package/.codex/skills/team-frontend/SKILL.md +227 -227
  77. package/.codex/skills/team-frontend-debug/SKILL.md +278 -278
  78. package/.codex/skills/team-frontend-debug/roles/coordinator/commands/analyze.md +2 -2
  79. package/.codex/skills/team-interactive-craft/SKILL.md +220 -220
  80. package/.codex/skills/team-interactive-craft/roles/coordinator/role.md +209 -209
  81. package/.codex/skills/team-issue/SKILL.md +269 -269
  82. package/.codex/skills/team-issue/roles/coordinator/role.md +1 -1
  83. package/.codex/skills/team-lifecycle-v4/SKILL.md +305 -305
  84. package/.codex/skills/team-motion-design/SKILL.md +222 -222
  85. package/.codex/skills/team-motion-design/roles/coordinator/role.md +210 -210
  86. package/.codex/skills/team-perf-opt/SKILL.md +258 -258
  87. package/.codex/skills/team-perf-opt/specs/team-config.json +1 -1
  88. package/.codex/skills/team-planex/SKILL.md +216 -216
  89. package/.codex/skills/team-quality-assurance/SKILL.md +229 -229
  90. package/.codex/skills/team-review/SKILL.md +227 -227
  91. package/.codex/skills/team-roadmap-dev/SKILL.md +238 -238
  92. package/.codex/skills/team-roadmap-dev/roles/coordinator/commands/roadmap-discuss.md +5 -5
  93. package/.codex/skills/team-tech-debt/SKILL.md +206 -206
  94. package/.codex/skills/team-tech-debt/roles/coordinator/commands/monitor.md +1 -1
  95. package/.codex/skills/team-testing/SKILL.md +237 -237
  96. package/.codex/skills/team-ui-polish/SKILL.md +218 -218
  97. package/.codex/skills/team-ui-polish/roles/coordinator/role.md +213 -213
  98. package/.codex/skills/team-uidesign/SKILL.md +219 -219
  99. package/.codex/skills/team-uidesign/roles/coordinator/role.md +2 -2
  100. package/.codex/skills/team-ultra-analyze/SKILL.md +260 -260
  101. package/.codex/skills/team-ultra-analyze/roles/coordinator/commands/monitor.md +1 -1
  102. package/.codex/skills/team-ultra-analyze/roles/coordinator/role.md +1 -1
  103. package/.codex/skills/team-ux-improve/SKILL.md +227 -227
  104. package/.codex/skills/team-ux-improve/roles/coordinator/role.md +1 -1
  105. package/.codex/skills/team-ux-improve/specs/team-config.json +1 -1
  106. package/.codex/skills/team-visual-a11y/SKILL.md +319 -319
  107. package/.codex/skills/team-visual-a11y/roles/coordinator/role.md +213 -213
  108. package/.codex/skills/workflow-execute/SKILL.md +5 -5
  109. package/.codex/skills/workflow-lite-planex/SKILL.md +3 -3
  110. package/.codex/skills/workflow-plan/SKILL.md +3 -3
  111. package/.codex/skills/workflow-tdd-plan/SKILL.md +4 -4
  112. package/.codex/skills/workflow-test-fix-cycle/SKILL.md +403 -402
  113. package/README.md +14 -0
  114. package/ccw/dist/cli.d.ts.map +1 -1
  115. package/ccw/dist/cli.js +16 -0
  116. package/ccw/dist/cli.js.map +1 -1
  117. package/ccw/dist/commands/chain-loader.d.ts +2 -0
  118. package/ccw/dist/commands/chain-loader.d.ts.map +1 -0
  119. package/ccw/dist/commands/chain-loader.js +11 -0
  120. package/ccw/dist/commands/chain-loader.js.map +1 -0
  121. package/ccw/dist/commands/install.d.ts.map +1 -1
  122. package/ccw/dist/commands/install.js +52 -1
  123. package/ccw/dist/commands/install.js.map +1 -1
  124. package/ccw/dist/commands/launcher.d.ts +2 -0
  125. package/ccw/dist/commands/launcher.d.ts.map +1 -0
  126. package/ccw/dist/commands/launcher.js +434 -0
  127. package/ccw/dist/commands/launcher.js.map +1 -0
  128. package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -1
  129. package/ccw/dist/core/routes/litellm-api-routes.js +0 -23
  130. package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -1
  131. package/ccw/dist/tools/chain-loader.d.ts +10 -0
  132. package/ccw/dist/tools/chain-loader.d.ts.map +1 -0
  133. package/ccw/dist/tools/chain-loader.js +1054 -0
  134. package/ccw/dist/tools/chain-loader.js.map +1 -0
  135. package/ccw/dist/tools/index.d.ts.map +1 -1
  136. package/ccw/dist/tools/index.js +2 -0
  137. package/ccw/dist/tools/index.js.map +1 -1
  138. package/ccw/dist/tools/json-builder.js +20 -0
  139. package/ccw/dist/tools/json-builder.js.map +1 -1
  140. package/ccw/dist/tools/skill-context-loader.d.ts.map +1 -1
  141. package/ccw/dist/tools/skill-context-loader.js +12 -26
  142. package/ccw/dist/tools/skill-context-loader.js.map +1 -1
  143. package/ccw/dist/types/chain-types.d.ts +112 -0
  144. package/ccw/dist/types/chain-types.d.ts.map +1 -0
  145. package/ccw/dist/types/chain-types.js +5 -0
  146. package/ccw/dist/types/chain-types.js.map +1 -0
  147. package/ccw/dist/utils/chain-visualizer.d.ts +13 -0
  148. package/ccw/dist/utils/chain-visualizer.d.ts.map +1 -0
  149. package/ccw/dist/utils/chain-visualizer.js +164 -0
  150. package/ccw/dist/utils/chain-visualizer.js.map +1 -0
  151. package/ccw/scripts/prepublish-clean.mjs +0 -1
  152. package/package.json +1 -3
  153. package/.claude/commands/cli/cli-init.md +0 -441
  154. package/.claude/commands/cli/codex-review.md +0 -361
  155. package/.claude/commands/flow-create.md +0 -663
  156. package/.claude/skills/team-edict.zip +0 -0
  157. package/ccw-litellm/README.md +0 -180
  158. package/ccw-litellm/pyproject.toml +0 -35
  159. package/ccw-litellm/src/ccw_litellm/__init__.py +0 -47
  160. package/ccw-litellm/src/ccw_litellm/cli.py +0 -108
  161. package/ccw-litellm/src/ccw_litellm/clients/__init__.py +0 -12
  162. package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +0 -270
  163. package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +0 -198
  164. package/ccw-litellm/src/ccw_litellm/config/__init__.py +0 -22
  165. package/ccw-litellm/src/ccw_litellm/config/loader.py +0 -343
  166. package/ccw-litellm/src/ccw_litellm/config/models.py +0 -162
  167. package/ccw-litellm/src/ccw_litellm/interfaces/__init__.py +0 -14
  168. package/ccw-litellm/src/ccw_litellm/interfaces/embedder.py +0 -52
  169. package/ccw-litellm/src/ccw_litellm/interfaces/llm.py +0 -45
@@ -1,180 +0,0 @@
1
- # ccw-litellm
2
-
3
- Unified LiteLLM interface layer shared by ccw and codex-lens projects.
4
-
5
- ## Features
6
-
7
- - **Unified LLM Interface**: Abstract interface for LLM operations (chat, completion)
8
- - **Unified Embedding Interface**: Abstract interface for text embeddings
9
- - **Multi-Provider Support**: OpenAI, Anthropic, Azure, and more via LiteLLM
10
- - **Configuration Management**: YAML-based configuration with environment variable substitution
11
- - **Type Safety**: Full type annotations with Pydantic models
12
-
13
- ## Installation
14
-
15
- ```bash
16
- pip install -e .
17
- ```
18
-
19
- ## Quick Start
20
-
21
- ### Configuration
22
-
23
- Create a configuration file at `~/.ccw/config/litellm-config.yaml`:
24
-
25
- ```yaml
26
- version: 1
27
- default_provider: openai
28
-
29
- providers:
30
- openai:
31
- api_key: ${OPENAI_API_KEY}
32
- api_base: https://api.openai.com/v1
33
-
34
- llm_models:
35
- default:
36
- provider: openai
37
- model: gpt-4
38
-
39
- embedding_models:
40
- default:
41
- provider: openai
42
- model: text-embedding-3-small
43
- dimensions: 1536
44
- ```
45
-
46
- ### Usage
47
-
48
- #### LLM Client
49
-
50
- ```python
51
- from ccw_litellm import LiteLLMClient, ChatMessage
52
-
53
- # Initialize client with default model
54
- client = LiteLLMClient(model="default")
55
-
56
- # Chat completion
57
- messages = [
58
- ChatMessage(role="user", content="Hello, how are you?")
59
- ]
60
- response = client.chat(messages)
61
- print(response.content)
62
-
63
- # Text completion
64
- response = client.complete("Once upon a time")
65
- print(response.content)
66
- ```
67
-
68
- #### Embedder
69
-
70
- ```python
71
- from ccw_litellm import LiteLLMEmbedder
72
-
73
- # Initialize embedder with default model
74
- embedder = LiteLLMEmbedder(model="default")
75
-
76
- # Embed single text
77
- vector = embedder.embed("Hello world")
78
- print(vector.shape) # (1, 1536)
79
-
80
- # Embed multiple texts
81
- vectors = embedder.embed(["Text 1", "Text 2", "Text 3"])
82
- print(vectors.shape) # (3, 1536)
83
- ```
84
-
85
- #### Custom Configuration
86
-
87
- ```python
88
- from ccw_litellm import LiteLLMClient, load_config
89
-
90
- # Load custom configuration
91
- config = load_config("/path/to/custom-config.yaml")
92
-
93
- # Use custom configuration
94
- client = LiteLLMClient(model="fast", config=config)
95
- ```
96
-
97
- ## Configuration Reference
98
-
99
- ### Provider Configuration
100
-
101
- ```yaml
102
- providers:
103
- <provider_name>:
104
- api_key: <api_key_or_${ENV_VAR}>
105
- api_base: <base_url>
106
- ```
107
-
108
- Supported providers: `openai`, `anthropic`, `azure`, `vertex_ai`, `bedrock`, etc.
109
-
110
- ### LLM Model Configuration
111
-
112
- ```yaml
113
- llm_models:
114
- <model_name>:
115
- provider: <provider_name>
116
- model: <model_identifier>
117
- ```
118
-
119
- ### Embedding Model Configuration
120
-
121
- ```yaml
122
- embedding_models:
123
- <model_name>:
124
- provider: <provider_name>
125
- model: <model_identifier>
126
- dimensions: <embedding_dimensions>
127
- ```
128
-
129
- ## Environment Variables
130
-
131
- The configuration supports environment variable substitution using the `${VAR}` or `${VAR:-default}` syntax:
132
-
133
- ```yaml
134
- providers:
135
- openai:
136
- api_key: ${OPENAI_API_KEY} # Required
137
- api_base: ${OPENAI_API_BASE:-https://api.openai.com/v1} # With default
138
- ```
139
-
140
- ## API Reference
141
-
142
- ### Interfaces
143
-
144
- - `AbstractLLMClient`: Abstract base class for LLM clients
145
- - `AbstractEmbedder`: Abstract base class for embedders
146
- - `ChatMessage`: Message data class (role, content)
147
- - `LLMResponse`: Response data class (content, raw)
148
-
149
- ### Implementations
150
-
151
- - `LiteLLMClient`: LiteLLM implementation of AbstractLLMClient
152
- - `LiteLLMEmbedder`: LiteLLM implementation of AbstractEmbedder
153
-
154
- ### Configuration
155
-
156
- - `LiteLLMConfig`: Root configuration model
157
- - `ProviderConfig`: Provider configuration model
158
- - `LLMModelConfig`: LLM model configuration model
159
- - `EmbeddingModelConfig`: Embedding model configuration model
160
- - `load_config(path)`: Load configuration from YAML file
161
- - `get_config(path, reload)`: Get global configuration singleton
162
- - `reset_config()`: Reset global configuration (for testing)
163
-
164
- ## Development
165
-
166
- ### Running Tests
167
-
168
- ```bash
169
- pytest tests/ -v
170
- ```
171
-
172
- ### Type Checking
173
-
174
- ```bash
175
- mypy src/ccw_litellm
176
- ```
177
-
178
- ## License
179
-
180
- MIT
@@ -1,35 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=61.0"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "ccw-litellm"
7
- version = "0.1.0"
8
- description = "Unified LiteLLM interface layer shared by ccw and codex-lens"
9
- requires-python = ">=3.10"
10
- authors = [{ name = "ccw-litellm contributors" }]
11
- dependencies = [
12
- "litellm>=1.0.0",
13
- "pyyaml",
14
- "numpy",
15
- "pydantic>=2.0",
16
- ]
17
-
18
- [project.optional-dependencies]
19
- dev = [
20
- "pytest>=7.0",
21
- ]
22
-
23
- [project.scripts]
24
- ccw-litellm = "ccw_litellm.cli:main"
25
-
26
- [tool.setuptools]
27
- package-dir = { "" = "src" }
28
-
29
- [tool.setuptools.packages.find]
30
- where = ["src"]
31
- include = ["ccw_litellm*"]
32
-
33
- [tool.pytest.ini_options]
34
- testpaths = ["tests"]
35
- addopts = "-q"
@@ -1,47 +0,0 @@
1
- """ccw-litellm package.
2
-
3
- This package provides a small, stable interface layer around LiteLLM to share
4
- between the ccw and codex-lens projects.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- from .clients import LiteLLMClient, LiteLLMEmbedder
10
- from .config import (
11
- EmbeddingModelConfig,
12
- LiteLLMConfig,
13
- LLMModelConfig,
14
- ProviderConfig,
15
- get_config,
16
- load_config,
17
- reset_config,
18
- )
19
- from .interfaces import (
20
- AbstractEmbedder,
21
- AbstractLLMClient,
22
- ChatMessage,
23
- LLMResponse,
24
- )
25
-
26
- __version__ = "0.1.0"
27
-
28
- __all__ = [
29
- "__version__",
30
- # Abstract interfaces
31
- "AbstractEmbedder",
32
- "AbstractLLMClient",
33
- "ChatMessage",
34
- "LLMResponse",
35
- # Client implementations
36
- "LiteLLMClient",
37
- "LiteLLMEmbedder",
38
- # Configuration
39
- "LiteLLMConfig",
40
- "ProviderConfig",
41
- "LLMModelConfig",
42
- "EmbeddingModelConfig",
43
- "load_config",
44
- "get_config",
45
- "reset_config",
46
- ]
47
-
@@ -1,108 +0,0 @@
1
- """CLI entry point for ccw-litellm."""
2
-
3
- from __future__ import annotations
4
-
5
- import argparse
6
- import json
7
- import sys
8
- from pathlib import Path
9
-
10
-
11
- def main() -> int:
12
- """Main CLI entry point."""
13
- parser = argparse.ArgumentParser(
14
- prog="ccw-litellm",
15
- description="Unified LiteLLM interface for ccw and codex-lens",
16
- )
17
- subparsers = parser.add_subparsers(dest="command", help="Available commands")
18
-
19
- # config command
20
- config_parser = subparsers.add_parser("config", help="Show configuration")
21
- config_parser.add_argument(
22
- "--path",
23
- type=Path,
24
- help="Configuration file path",
25
- )
26
-
27
- # embed command
28
- embed_parser = subparsers.add_parser("embed", help="Generate embeddings")
29
- embed_parser.add_argument("texts", nargs="+", help="Texts to embed")
30
- embed_parser.add_argument(
31
- "--model",
32
- default="default",
33
- help="Embedding model name (default: default)",
34
- )
35
- embed_parser.add_argument(
36
- "--output",
37
- choices=["json", "shape"],
38
- default="shape",
39
- help="Output format (default: shape)",
40
- )
41
-
42
- # chat command
43
- chat_parser = subparsers.add_parser("chat", help="Chat with LLM")
44
- chat_parser.add_argument("message", help="Message to send")
45
- chat_parser.add_argument(
46
- "--model",
47
- default="default",
48
- help="LLM model name (default: default)",
49
- )
50
-
51
- # version command
52
- subparsers.add_parser("version", help="Show version")
53
-
54
- args = parser.parse_args()
55
-
56
- if args.command == "version":
57
- from . import __version__
58
-
59
- print(f"ccw-litellm {__version__}")
60
- return 0
61
-
62
- if args.command == "config":
63
- from .config import get_config
64
-
65
- try:
66
- config = get_config(config_path=args.path if hasattr(args, "path") else None)
67
- print(config.model_dump_json(indent=2))
68
- except Exception as e:
69
- print(f"Error loading config: {e}", file=sys.stderr)
70
- return 1
71
- return 0
72
-
73
- if args.command == "embed":
74
- from .clients import LiteLLMEmbedder
75
-
76
- try:
77
- embedder = LiteLLMEmbedder(model=args.model)
78
- vectors = embedder.embed(args.texts)
79
-
80
- if args.output == "json":
81
- print(json.dumps(vectors.tolist()))
82
- else:
83
- print(f"Shape: {vectors.shape}")
84
- print(f"Dimensions: {embedder.dimensions}")
85
- except Exception as e:
86
- print(f"Error: {e}", file=sys.stderr)
87
- return 1
88
- return 0
89
-
90
- if args.command == "chat":
91
- from .clients import LiteLLMClient
92
- from .interfaces import ChatMessage
93
-
94
- try:
95
- client = LiteLLMClient(model=args.model)
96
- response = client.chat([ChatMessage(role="user", content=args.message)])
97
- print(response.content)
98
- except Exception as e:
99
- print(f"Error: {e}", file=sys.stderr)
100
- return 1
101
- return 0
102
-
103
- parser.print_help()
104
- return 0
105
-
106
-
107
- if __name__ == "__main__":
108
- sys.exit(main())
@@ -1,12 +0,0 @@
1
- """Client implementations for ccw-litellm."""
2
-
3
- from __future__ import annotations
4
-
5
- from .litellm_embedder import LiteLLMEmbedder
6
- from .litellm_llm import LiteLLMClient
7
-
8
- __all__ = [
9
- "LiteLLMClient",
10
- "LiteLLMEmbedder",
11
- ]
12
-
@@ -1,270 +0,0 @@
1
- """LiteLLM embedder implementation for text embeddings."""
2
-
3
- from __future__ import annotations
4
-
5
- import logging
6
- from typing import Any, Sequence
7
-
8
- import litellm
9
- import numpy as np
10
- from numpy.typing import NDArray
11
-
12
- from ..config import LiteLLMConfig, get_config
13
- from ..interfaces.embedder import AbstractEmbedder
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
-
18
- class LiteLLMEmbedder(AbstractEmbedder):
19
- """LiteLLM embedder implementation.
20
-
21
- Supports multiple embedding providers (OpenAI, etc.) through LiteLLM's unified interface.
22
-
23
- Example:
24
- embedder = LiteLLMEmbedder(model="default")
25
- vectors = embedder.embed(["Hello world", "Another text"])
26
- print(vectors.shape) # (2, 1536)
27
- """
28
-
29
- def __init__(
30
- self,
31
- model: str = "default",
32
- config: LiteLLMConfig | None = None,
33
- **litellm_kwargs: Any,
34
- ) -> None:
35
- """Initialize LiteLLM embedder.
36
-
37
- Args:
38
- model: Model name from configuration (default: "default")
39
- config: Configuration instance (default: use global config)
40
- **litellm_kwargs: Additional arguments to pass to litellm.embedding()
41
- """
42
- self._config = config or get_config()
43
- self._model_name = model
44
- self._litellm_kwargs = litellm_kwargs
45
-
46
- # Get embedding model configuration
47
- try:
48
- self._model_config = self._config.get_embedding_model(model)
49
- except ValueError as e:
50
- logger.error(f"Failed to get embedding model configuration: {e}")
51
- raise
52
-
53
- # Get provider configuration
54
- try:
55
- self._provider_config = self._config.get_provider(self._model_config.provider)
56
- except ValueError as e:
57
- logger.error(f"Failed to get provider configuration: {e}")
58
- raise
59
-
60
- # Set up LiteLLM environment
61
- self._setup_litellm()
62
-
63
- def _setup_litellm(self) -> None:
64
- """Configure LiteLLM with provider settings."""
65
- provider = self._model_config.provider
66
-
67
- # Set API key
68
- if self._provider_config.api_key:
69
- litellm.api_key = self._provider_config.api_key
70
- # Also set environment-specific keys
71
- if provider == "openai":
72
- litellm.openai_key = self._provider_config.api_key
73
- elif provider == "anthropic":
74
- litellm.anthropic_key = self._provider_config.api_key
75
-
76
- # Set API base
77
- if self._provider_config.api_base:
78
- litellm.api_base = self._provider_config.api_base
79
-
80
- def _format_model_name(self) -> str:
81
- """Format model name for LiteLLM.
82
-
83
- Returns:
84
- Formatted model name (e.g., "openai/text-embedding-3-small")
85
- """
86
- provider = self._model_config.provider
87
- model = self._model_config.model
88
-
89
- # For some providers, LiteLLM expects explicit prefix
90
- if provider in ["azure", "vertex_ai", "bedrock"]:
91
- return f"{provider}/{model}"
92
-
93
- # For providers with custom api_base (OpenAI-compatible endpoints),
94
- # use openai/ prefix to tell LiteLLM to use OpenAI API format
95
- if self._provider_config.api_base and provider not in ["openai", "anthropic"]:
96
- return f"openai/{model}"
97
-
98
- return model
99
-
100
- @property
101
- def dimensions(self) -> int:
102
- """Embedding vector size."""
103
- return self._model_config.dimensions
104
-
105
- @property
106
- def max_input_tokens(self) -> int:
107
- """Maximum token limit for embeddings.
108
-
109
- Returns the configured max_input_tokens from model config,
110
- enabling adaptive batch sizing based on actual model capacity.
111
- """
112
- return self._model_config.max_input_tokens
113
-
114
- def _estimate_tokens(self, text: str) -> int:
115
- """Estimate token count for a text using fast heuristic.
116
-
117
- Args:
118
- text: Text to estimate tokens for
119
-
120
- Returns:
121
- Estimated token count (len/4 is a reasonable approximation)
122
- """
123
- return len(text) // 4
124
-
125
- def _create_batches(
126
- self,
127
- texts: list[str],
128
- max_tokens: int = 30000
129
- ) -> list[list[str]]:
130
- """Split texts into batches that fit within token limits.
131
-
132
- Args:
133
- texts: List of texts to batch
134
- max_tokens: Maximum tokens per batch (default: 30000, safe margin for 40960 limit)
135
-
136
- Returns:
137
- List of text batches
138
- """
139
- batches = []
140
- current_batch = []
141
- current_tokens = 0
142
-
143
- for text in texts:
144
- text_tokens = self._estimate_tokens(text)
145
-
146
- # If single text exceeds limit, truncate it
147
- if text_tokens > max_tokens:
148
- logger.warning(f"Text with {text_tokens} estimated tokens exceeds limit, truncating")
149
- # Truncate to fit (rough estimate: 4 chars per token)
150
- max_chars = max_tokens * 4
151
- text = text[:max_chars]
152
- text_tokens = self._estimate_tokens(text)
153
-
154
- # Start new batch if current would exceed limit
155
- if current_tokens + text_tokens > max_tokens and current_batch:
156
- batches.append(current_batch)
157
- current_batch = []
158
- current_tokens = 0
159
-
160
- current_batch.append(text)
161
- current_tokens += text_tokens
162
-
163
- # Add final batch
164
- if current_batch:
165
- batches.append(current_batch)
166
-
167
- return batches
168
-
169
- def embed(
170
- self,
171
- texts: str | Sequence[str],
172
- *,
173
- batch_size: int | None = None,
174
- max_tokens_per_batch: int | None = None,
175
- **kwargs: Any,
176
- ) -> NDArray[np.floating]:
177
- """Embed one or more texts.
178
-
179
- Args:
180
- texts: Single text or sequence of texts
181
- batch_size: Batch size for processing (deprecated, use max_tokens_per_batch)
182
- max_tokens_per_batch: Maximum estimated tokens per API call.
183
- If None, uses 90% of model's max_input_tokens for safety margin.
184
- **kwargs: Additional arguments for litellm.embedding()
185
-
186
- Returns:
187
- A numpy array of shape (n_texts, dimensions).
188
-
189
- Raises:
190
- Exception: If LiteLLM embedding fails
191
- """
192
- # Normalize input to list
193
- if isinstance(texts, str):
194
- text_list = [texts]
195
- else:
196
- text_list = list(texts)
197
-
198
- if not text_list:
199
- # Return empty array with correct shape
200
- return np.empty((0, self.dimensions), dtype=np.float32)
201
-
202
- # Merge kwargs
203
- embedding_kwargs = {**self._litellm_kwargs, **kwargs}
204
-
205
- # For OpenAI-compatible endpoints, ensure encoding_format is set
206
- if self._provider_config.api_base and "encoding_format" not in embedding_kwargs:
207
- embedding_kwargs["encoding_format"] = "float"
208
-
209
- # Determine adaptive max_tokens_per_batch
210
- # Use 90% of model's max_input_tokens as safety margin
211
- if max_tokens_per_batch is None:
212
- max_tokens_per_batch = int(self.max_input_tokens * 0.9)
213
- logger.debug(
214
- f"Using adaptive batch size: {max_tokens_per_batch} tokens "
215
- f"(90% of {self.max_input_tokens})"
216
- )
217
-
218
- # Split into token-aware batches
219
- batches = self._create_batches(text_list, max_tokens_per_batch)
220
-
221
- if len(batches) > 1:
222
- logger.info(f"Split {len(text_list)} texts into {len(batches)} batches for embedding")
223
-
224
- all_embeddings = []
225
-
226
- for batch_idx, batch in enumerate(batches):
227
- try:
228
- # Build call kwargs with explicit api_base
229
- call_kwargs = {**embedding_kwargs}
230
- if self._provider_config.api_base:
231
- call_kwargs["api_base"] = self._provider_config.api_base
232
- if self._provider_config.api_key:
233
- call_kwargs["api_key"] = self._provider_config.api_key
234
-
235
- # Call LiteLLM embedding for this batch
236
- response = litellm.embedding(
237
- model=self._format_model_name(),
238
- input=batch,
239
- **call_kwargs,
240
- )
241
-
242
- # Extract embeddings
243
- batch_embeddings = [item["embedding"] for item in response.data]
244
- all_embeddings.extend(batch_embeddings)
245
-
246
- except Exception as e:
247
- logger.error(f"LiteLLM embedding failed for batch {batch_idx + 1}/{len(batches)}: {e}")
248
- raise
249
-
250
- # Convert to numpy array
251
- result = np.array(all_embeddings, dtype=np.float32)
252
-
253
- # Validate dimensions
254
- if result.shape[1] != self.dimensions:
255
- logger.warning(
256
- f"Expected {self.dimensions} dimensions, got {result.shape[1]}. "
257
- f"Configuration may be incorrect."
258
- )
259
-
260
- return result
261
-
262
- @property
263
- def model_name(self) -> str:
264
- """Get configured model name."""
265
- return self._model_name
266
-
267
- @property
268
- def provider(self) -> str:
269
- """Get configured provider name."""
270
- return self._model_config.provider