spawnio 1.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. spawn/__init__.py +6 -0
  2. spawn/cli/__init__.py +0 -0
  3. spawn/cli/app.py +218 -0
  4. spawn/cli/noninteractive.py +204 -0
  5. spawn/cli/prompts.py +267 -0
  6. spawn/core/__init__.py +0 -0
  7. spawn/core/exceptions.py +7 -0
  8. spawn/core/models.py +21 -0
  9. spawn/core/registry.py +152 -0
  10. spawn/generators/__init__.py +0 -0
  11. spawn/generators/custom_structure.py +508 -0
  12. spawn/generators/project_generator.py +115 -0
  13. spawn/github/__init__.py +0 -0
  14. spawn/github/exceptions.py +5 -0
  15. spawn/github/publisher.py +69 -0
  16. spawn/github/validators.py +23 -0
  17. spawn/templates/__init__.py +0 -0
  18. spawn/templates/agent/__init__.py +182 -0
  19. spawn/templates/agent/content.py +374 -0
  20. spawn/templates/automation/__init__.py +100 -0
  21. spawn/templates/automation/content.py +181 -0
  22. spawn/templates/backend_api/__init__.py +243 -0
  23. spawn/templates/backend_api/content.py +509 -0
  24. spawn/templates/base.py +58 -0
  25. spawn/templates/chatbot/__init__.py +243 -0
  26. spawn/templates/chatbot/content.py +668 -0
  27. spawn/templates/cli_application/__init__.py +211 -0
  28. spawn/templates/cli_application/content.py +512 -0
  29. spawn/templates/data_project/__init__.py +125 -0
  30. spawn/templates/data_project/analysis/__init__.py +74 -0
  31. spawn/templates/data_project/analysis/content.py +190 -0
  32. spawn/templates/data_project/dashboard/__init__.py +61 -0
  33. spawn/templates/data_project/dashboard/content.py +135 -0
  34. spawn/templates/data_project/etl/__init__.py +66 -0
  35. spawn/templates/data_project/etl/content.py +239 -0
  36. spawn/templates/data_project/ml/__init__.py +62 -0
  37. spawn/templates/data_project/ml/content.py +239 -0
  38. spawn/templates/mcp_server/__init__.py +83 -0
  39. spawn/templates/mcp_server/content.py +161 -0
  40. spawn/templates/rag/__init__.py +124 -0
  41. spawn/templates/rag/content.py +463 -0
  42. spawn/templates/shared_content.py +89 -0
  43. spawn/utils/__init__.py +0 -0
  44. spawn/utils/banner.py +41 -0
  45. spawn/utils/console.py +3 -0
  46. spawn/utils/doctor.py +594 -0
  47. spawn/utils/git.py +132 -0
  48. spawn/utils/success.py +47 -0
  49. spawn/utils/theme.py +25 -0
  50. spawn/utils/uv.py +61 -0
  51. spawn/utils/validators.py +15 -0
  52. spawnio-1.0.5.dist-info/METADATA +798 -0
  53. spawnio-1.0.5.dist-info/RECORD +56 -0
  54. spawnio-1.0.5.dist-info/WHEEL +4 -0
  55. spawnio-1.0.5.dist-info/entry_points.txt +2 -0
  56. spawnio-1.0.5.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,243 @@
1
+ from pathlib import Path
2
+
3
+ from spawn.templates.base import BaseTemplate
4
+ from spawn.templates.chatbot.content import (
5
+ INIT_CONTENT,
6
+ CHAT_CONTENT,
7
+ MEMORY_HISTORY_CONTENT_BASE,
8
+ MEMORY_HISTORY_CONTENT_PYDANTIC,
9
+ SYSTEM_PROMPT_TXT_CONTENT,
10
+ SETTINGS_CONTENT,
11
+ CONFTEST_CONTENT,
12
+ MAIN_CONTENT_NO_RICH,
13
+ MAIN_CONTENT_RICH,
14
+ TEST_CONTENT,
15
+ GITHUB_ACTIONS_CI_BASE,
16
+ GITHUB_ACTIONS_CI_RUFF_STEP,
17
+ GITHUB_ACTIONS_CI_PYTEST_STEP,
18
+ PYDANTIC_AI_OPENAI_LLM_CONTENT,
19
+ PYDANTIC_AI_ANTHROPIC_LLM_CONTENT,
20
+ PYDANTIC_AI_GEMINI_LLM_CONTENT,
21
+ PYDANTIC_AI_OPENROUTER_LLM_CONTENT,
22
+ PYDANTIC_AI_OLLAMA_LLM_CONTENT,
23
+ PYDANTIC_AI_GROQ_LLM_CONTENT,
24
+ OPENAI_SDK_OPENAI_LLM_CONTENT,
25
+ OPENAI_SDK_OPENROUTER_LLM_CONTENT,
26
+ OPENAI_SDK_GEMINI_LLM_CONTENT,
27
+ OPENAI_SDK_GROQ_LLM_CONTENT,
28
+ LITELLM_OPENAI_LLM_CONTENT,
29
+ LITELLM_ANTHROPIC_LLM_CONTENT,
30
+ LITELLM_GEMINI_LLM_CONTENT,
31
+ LITELLM_OPENROUTER_LLM_CONTENT,
32
+ LITELLM_OLLAMA_LLM_CONTENT,
33
+ LITELLM_GROQ_LLM_CONTENT,
34
+ ENV_OPENAI,
35
+ ENV_ANTHROPIC,
36
+ ENV_GEMINI,
37
+ ENV_OPENROUTER,
38
+ ENV_OLLAMA,
39
+ ENV_LITELLM_OLLAMA,
40
+ ENV_GROQ,
41
+ ENV_PYDANTIC_OPENAI,
42
+ ENV_PYDANTIC_ANTHROPIC,
43
+ ENV_PYDANTIC_GEMINI,
44
+ ENV_PYDANTIC_OPENROUTER,
45
+ ENV_PYDANTIC_OLLAMA,
46
+ ENV_PYDANTIC_GROQ,
47
+ ENV_LITELLM_GROQ,
48
+ make_readme,
49
+ make_agents_md,
50
+ )
51
+
52
+ CHATBOT_FOLDERS = [
53
+ "src/chatbot",
54
+ "src/providers",
55
+ "src/prompts",
56
+ "src/memory",
57
+ "src/config",
58
+ "tests",
59
+ ]
60
+
61
+ # Maps (framework, provider) → llm content
62
+ _LLM_MAP: dict[tuple[str, str], str] = {
63
+ ("pydantic-ai", "openai"): PYDANTIC_AI_OPENAI_LLM_CONTENT,
64
+ ("pydantic-ai", "anthropic"): PYDANTIC_AI_ANTHROPIC_LLM_CONTENT,
65
+ ("pydantic-ai", "gemini"): PYDANTIC_AI_GEMINI_LLM_CONTENT,
66
+ ("pydantic-ai", "openrouter"): PYDANTIC_AI_OPENROUTER_LLM_CONTENT,
67
+ ("pydantic-ai", "ollama"): PYDANTIC_AI_OLLAMA_LLM_CONTENT,
68
+ ("pydantic-ai", "groq"): PYDANTIC_AI_GROQ_LLM_CONTENT,
69
+ ("openai-sdk", "openai"): OPENAI_SDK_OPENAI_LLM_CONTENT,
70
+ ("openai-sdk", "openrouter"): OPENAI_SDK_OPENROUTER_LLM_CONTENT,
71
+ ("openai-sdk", "gemini"): OPENAI_SDK_GEMINI_LLM_CONTENT,
72
+ ("openai-sdk", "groq"): OPENAI_SDK_GROQ_LLM_CONTENT,
73
+ ("litellm", "openai"): LITELLM_OPENAI_LLM_CONTENT,
74
+ ("litellm", "anthropic"): LITELLM_ANTHROPIC_LLM_CONTENT,
75
+ ("litellm", "gemini"): LITELLM_GEMINI_LLM_CONTENT,
76
+ ("litellm", "openrouter"): LITELLM_OPENROUTER_LLM_CONTENT,
77
+ ("litellm", "ollama"): LITELLM_OLLAMA_LLM_CONTENT,
78
+ ("litellm", "groq"): LITELLM_GROQ_LLM_CONTENT,
79
+ }
80
+
81
+ # Maps provider → env example (pydantic-ai uses prefixed MODEL format)
82
+ _ENV_MAP_PYDANTIC: dict[str, str] = {
83
+ "openai": ENV_PYDANTIC_OPENAI,
84
+ "anthropic": ENV_PYDANTIC_ANTHROPIC,
85
+ "gemini": ENV_PYDANTIC_GEMINI,
86
+ "openrouter": ENV_PYDANTIC_OPENROUTER,
87
+ "ollama": ENV_PYDANTIC_OLLAMA,
88
+ "groq": ENV_PYDANTIC_GROQ,
89
+ }
90
+
91
+ _ENV_MAP_GENERIC: dict[str, str] = {
92
+ "openai": ENV_OPENAI,
93
+ "anthropic": ENV_ANTHROPIC,
94
+ "gemini": ENV_GEMINI,
95
+ "openrouter": ENV_OPENROUTER,
96
+ "ollama": ENV_OLLAMA,
97
+ "groq": ENV_GROQ,
98
+ }
99
+
100
+ # litellm+ollama uses OLLAMA_API_BASE (litellm convention) instead of OLLAMA_BASE_URL
101
+ _ENV_MAP_LITELLM: dict[str, str] = {
102
+ "openai": ENV_OPENAI,
103
+ "anthropic": ENV_ANTHROPIC,
104
+ "gemini": ENV_GEMINI,
105
+ "openrouter": ENV_OPENROUTER,
106
+ "ollama": ENV_LITELLM_OLLAMA,
107
+ "groq": ENV_LITELLM_GROQ,
108
+ }
109
+
110
+
111
+ def _resolve_llm(framework: str, provider: str) -> str:
112
+ return _LLM_MAP.get((framework, provider), PYDANTIC_AI_OPENAI_LLM_CONTENT)
113
+
114
+
115
+ def get_supported_providers(framework: str) -> list[str]:
116
+ """Return providers supported for a given framework, in menu order."""
117
+ order = ["openai", "anthropic", "gemini", "openrouter", "ollama", "groq"]
118
+ supported = {provider for (fw, provider) in _LLM_MAP if fw == framework}
119
+ return [p for p in order if p in supported]
120
+
121
+
122
+ def _resolve_env(framework: str, provider: str) -> str:
123
+ if framework == "pydantic-ai":
124
+ return _ENV_MAP_PYDANTIC.get(provider, ENV_PYDANTIC_OPENAI)
125
+ if framework == "litellm":
126
+ return _ENV_MAP_LITELLM.get(provider, ENV_OPENAI)
127
+ return _ENV_MAP_GENERIC.get(provider, ENV_OPENAI)
128
+
129
+
130
+ def _build_files(main_content: str, llm_content: str, env_content: str, memory_content: str) -> list:
131
+ return [
132
+ ("src/__init__.py", INIT_CONTENT),
133
+ ("src/chatbot/__init__.py", INIT_CONTENT),
134
+ ("src/chatbot/chat.py", CHAT_CONTENT),
135
+ ("src/providers/__init__.py", INIT_CONTENT),
136
+ ("src/providers/llm.py", llm_content),
137
+ ("src/prompts/__init__.py", INIT_CONTENT),
138
+ ("src/prompts/system.txt", SYSTEM_PROMPT_TXT_CONTENT),
139
+ ("src/memory/__init__.py", INIT_CONTENT),
140
+ ("src/memory/history.py", memory_content),
141
+ ("src/config/__init__.py", INIT_CONTENT),
142
+ ("src/config/settings.py", SETTINGS_CONTENT),
143
+ ("src/main.py", main_content),
144
+ ("tests/__init__.py", INIT_CONTENT),
145
+ ("tests/conftest.py", CONFTEST_CONTENT),
146
+ ("tests/test_chatbot.py", TEST_CONTENT),
147
+ (".env.example", env_content),
148
+ ]
149
+
150
+
151
+ class ChatbotTemplate(BaseTemplate):
152
+ def __init__(
153
+ self,
154
+ framework: str | None = None,
155
+ provider: str | None = None,
156
+ extras: list[str] | None = None,
157
+ ) -> None:
158
+ self.framework = framework or "pydantic-ai"
159
+ self.provider = provider or "openai"
160
+ self.extras = extras or []
161
+
162
+ llm_content = _resolve_llm(self.framework, self.provider)
163
+ env_content = _resolve_env(self.framework, self.provider)
164
+ main_content = MAIN_CONTENT_RICH if "rich" in self.extras else MAIN_CONTENT_NO_RICH
165
+ memory_content = (
166
+ MEMORY_HISTORY_CONTENT_PYDANTIC
167
+ if self.framework == "pydantic-ai"
168
+ else MEMORY_HISTORY_CONTENT_BASE
169
+ )
170
+
171
+ super().__init__(
172
+ name="AI Chatbot",
173
+ folders=list(CHATBOT_FOLDERS),
174
+ starter_files=_build_files(main_content, llm_content, env_content, memory_content),
175
+ next_steps=[
176
+ "cd {project_name}",
177
+ "Rename .env.example to .env and fill in your API key",
178
+ "uv run python -m src.main",
179
+ ],
180
+ )
181
+
182
+ def get_readme_content(self, context: dict) -> str | None:
183
+ raw = make_readme(self.framework, self.provider)
184
+ return raw.format_map(context)
185
+
186
+ def get_agents_md_content(self, context: dict) -> str | None:
187
+ raw = make_agents_md(self.framework, self.provider)
188
+ return raw.format_map(context)
189
+
190
+ def get_dependencies(self) -> list[str]:
191
+ dep_map: dict[tuple[str, str], list[str]] = {
192
+ ("pydantic-ai", "openai"): ["pydantic-ai", "python-dotenv"],
193
+ ("pydantic-ai", "anthropic"): ["pydantic-ai", "python-dotenv"],
194
+ ("pydantic-ai", "gemini"): ["pydantic-ai", "python-dotenv"],
195
+ ("pydantic-ai", "openrouter"): ["pydantic-ai", "python-dotenv"],
196
+ ("pydantic-ai", "ollama"): ["pydantic-ai", "python-dotenv"],
197
+ ("pydantic-ai", "groq"): ["pydantic-ai[groq]", "python-dotenv"],
198
+ ("openai-sdk", "openai"): ["openai", "python-dotenv"],
199
+ ("openai-sdk", "openrouter"): ["openai", "python-dotenv"],
200
+ ("openai-sdk", "gemini"): ["openai", "python-dotenv"],
201
+ ("openai-sdk", "groq"): ["openai", "python-dotenv"],
202
+ ("litellm", "openai"): ["litellm", "python-dotenv"],
203
+ ("litellm", "anthropic"): ["litellm", "python-dotenv"],
204
+ ("litellm", "gemini"): ["litellm", "python-dotenv"],
205
+ ("litellm", "openrouter"): ["litellm", "python-dotenv"],
206
+ ("litellm", "ollama"): ["litellm", "python-dotenv"],
207
+ ("litellm", "groq"): ["litellm", "python-dotenv"],
208
+ }
209
+
210
+ base = list(dep_map.get((self.framework, self.provider), ["pydantic-ai", "openai", "python-dotenv"]))
211
+
212
+ if "pytest" in self.extras:
213
+ base.append("pytest")
214
+ if "ruff" in self.extras:
215
+ base.append("ruff")
216
+ if "rich" in self.extras:
217
+ base.append("rich")
218
+
219
+ return base
220
+
221
+ def post_install(self, project_path: Path) -> None:
222
+ pyproject = project_path / "pyproject.toml"
223
+ current = pyproject.read_text(encoding="utf-8")
224
+ additions = ""
225
+
226
+ if "pytest" in self.extras:
227
+ additions += "\n[tool.pytest.ini_options]\ntestpaths = [\"tests\"]\n"
228
+
229
+ if "ruff" in self.extras:
230
+ additions += "\n[tool.ruff]\nline-length = 88\n"
231
+
232
+ if additions:
233
+ pyproject.write_text(current + additions, encoding="utf-8")
234
+
235
+ if "github-actions" in self.extras:
236
+ workflows_path = project_path / ".github" / "workflows"
237
+ workflows_path.mkdir(parents=True, exist_ok=True)
238
+ ci = GITHUB_ACTIONS_CI_BASE
239
+ if "ruff" in self.extras:
240
+ ci += GITHUB_ACTIONS_CI_RUFF_STEP
241
+ if "pytest" in self.extras:
242
+ ci += GITHUB_ACTIONS_CI_PYTEST_STEP
243
+ (workflows_path / "ci.yml").write_text(ci, encoding="utf-8")