Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions fabricate/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ def cli():

@cli.command()
@click.option(
"--anthropic-key", "-a",
envvar="FABRICATE_ANTHROPIC_API_KEY",
help="Anthropic API key (or set FABRICATE_ANTHROPIC_API_KEY)"
"--gemini-key", "-g",
envvar="FABRICATE_GEMINI_API_KEY",
help="Google Gemini API key (or set FABRICATE_GEMINI_API_KEY)"
)
@click.option(
"--github-token", "-g",
"--github-token", "-t",
envvar="FABRICATE_GITHUB_TOKEN",
help="GitHub personal access token (or set FABRICATE_GITHUB_TOKEN)"
)
Expand Down Expand Up @@ -99,7 +99,7 @@ def cli():
help="Show what would be created without actually creating"
)
@click.option(
"--tech", "-t",
"--tech",
multiple=True,
help="Technologies to include (e.g., tailwind, prisma, redis, docker)"
)
Expand All @@ -109,7 +109,7 @@ def cli():
help="Project categories to build (e.g., cli_tool, web_app, saas, dashboard, api)"
)
def generate(
anthropic_key: Optional[str],
gemini_key: Optional[str],
github_token: Optional[str],
languages: tuple,
repos: Optional[int],
Expand Down Expand Up @@ -139,16 +139,16 @@ def generate(
fabricate generate -l python -l javascript -l go -r 10 -d 730

# Specify technologies and project types
fabricate generate -l nextjs -t tailwind -t prisma -c saas -c dashboard
fabricate generate -l nextjs --tech tailwind --tech prisma -c saas -c dashboard

# Local only (no GitHub push)
fabricate generate --no-push -r 2
"""

# Validate required credentials
if not anthropic_key:
console.print("[red]Error: Anthropic API key required[/red]")
console.print("Set FABRICATE_ANTHROPIC_API_KEY or use --anthropic-key")
if not gemini_key:
console.print("[red]Error: Google Gemini API key required[/red]")
console.print("Set FABRICATE_GEMINI_API_KEY or use --gemini-key")
sys.exit(1)

if not github_token and not no_push:
Expand Down Expand Up @@ -196,7 +196,7 @@ def generate(
# Run the fabrication
try:
generated = run_fabrication(
anthropic_api_key=anthropic_key,
gemini_api_key=gemini_key,
github_token=github_token or "",
languages=languages_list,
num_repos=repos,
Expand Down Expand Up @@ -342,4 +342,3 @@ def main():

if __name__ == "__main__":
main()

3 changes: 1 addition & 2 deletions fabricate/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def generate_commit_date(self, repo_start_date: datetime, commit_index: int, tot
class FabricateSettings(BaseSettings):
"""Environment-based settings for Fabricate."""

anthropic_api_key: str = Field(description="Anthropic API key for code generation")
gemini_api_key: str = Field(description="Google Gemini API key for code generation")
github_token: str = Field(description="GitHub personal access token")
github_username: Optional[str] = Field(default=None, description="GitHub username (auto-detected if not set)")
work_dir: str = Field(default="./fabricated_repos", description="Directory for temporary repo work")
Expand Down Expand Up @@ -207,4 +207,3 @@ class Config:
"dashboard",
"saas",
]

38 changes: 20 additions & 18 deletions fabricate/generator.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"""
Code generation using Anthropic's Claude API.
Code generation using Google's Gemini 3.1 Pro API.
"""

import json
import random
from typing import Optional
from dataclasses import dataclass

import anthropic
import google.generativeai as genai
from rich.console import Console

from .config import LANGUAGE_CONFIGS, COMPLEXITY_PROFILES, PROJECT_CATEGORIES
Expand Down Expand Up @@ -42,24 +42,27 @@ class GeneratedRepo:


class CodeGenerator:
"""Generates code and repository content using Claude."""
"""Generates code and repository content using Gemini 3.1 Pro."""

def __init__(self, api_key: str, model: str = "claude-opus-4-5-20251101"):
self.client = anthropic.Anthropic(api_key=api_key)
def __init__(self, api_key: str, model: str = "gemini-3.1-pro-preview"):
genai.configure(api_key=api_key)
self.client = genai.GenerativeModel(model)
self.model = model

def _call_claude(self, system: str, user: str, max_tokens: int = 4096) -> str:
"""Make a call to Claude API."""
response = self.client.messages.create(
model=self.model,
max_tokens=max_tokens,
system=system,
messages=[{"role": "user", "content": user}]
def _call_gemini(self, system: str, user: str, max_tokens: int = 4096) -> str:
"""Make a call to Gemini API."""
combined_prompt = f"{system}\n\n{user}"
response = self.client.generate_content(
combined_prompt,
generation_config=genai.types.GenerationConfig(
max_output_tokens=max_tokens,
temperature=0.7,
)
)
return response.content[0].text
return response.text

def _extract_json(self, response: str) -> dict:
"""Extract JSON from Claude's response, handling various formats."""
"""Extract JSON from Gemini's response, handling various formats."""
if not response or not response.strip():
raise ValueError("Empty response")

Expand Down Expand Up @@ -148,7 +151,7 @@ def generate_repo_concept(
"main_features": ["feature1", "feature2", "feature3"]
}}"""

response = self._call_claude(system, user, max_tokens=500)
response = self._call_gemini(system, user, max_tokens=500)

try:
return self._extract_json(response)
Expand Down Expand Up @@ -215,7 +218,7 @@ def generate_initial_commit(
- Make the code realistic and functional
- Use proper formatting and indentation (use \\n for newlines, \\t for tabs)"""

response = self._call_claude(system, user, max_tokens=8000)
response = self._call_gemini(system, user, max_tokens=8000)

try:
data = self._extract_json(response)
Expand Down Expand Up @@ -368,7 +371,7 @@ def generate_subsequent_commit(
- Use conventional commit message format
- Content should be complete and valid {language} code"""

response = self._call_claude(system, user, max_tokens=6000)
response = self._call_gemini(system, user, max_tokens=6000)

try:
data = self._extract_json(response)
Expand Down Expand Up @@ -455,4 +458,3 @@ def generate_full_repo(
commits=commits,
topics=concept.get("topics", [language])
)

11 changes: 5 additions & 6 deletions fabricate/persona.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ class PersonaFabricator:

def __init__(
self,
anthropic_api_key: str,
gemini_api_key: str,
github_token: str,
github_username: Optional[str] = None,
work_dir: str = "./fabricated_repos",
author_name: Optional[str] = None,
author_email: Optional[str] = None
):
self.generator = CodeGenerator(anthropic_api_key)
self.generator = CodeGenerator(gemini_api_key)
self.github = GitHubClient(github_token, github_username)

# Get user info for author details
Expand Down Expand Up @@ -224,7 +224,7 @@ def _print_summary(self, repos: list[GeneratedRepo], config: PersonaConfig) -> N


def run_fabrication(
anthropic_api_key: str,
gemini_api_key: str,
github_token: str,
languages: list[str],
num_repos: int,
Expand All @@ -242,7 +242,7 @@ def run_fabrication(
Convenience function to run a complete fabrication.

Args:
anthropic_api_key: Anthropic API key for code generation
gemini_api_key: Google Gemini API key for code generation
github_token: GitHub personal access token
languages: List of programming languages to use
num_repos: Number of repositories to create
Expand Down Expand Up @@ -271,7 +271,7 @@ def run_fabrication(
)

fabricator = PersonaFabricator(
anthropic_api_key=anthropic_api_key,
gemini_api_key=gemini_api_key,
github_token=github_token,
github_username=github_username,
work_dir=work_dir
Expand All @@ -282,4 +282,3 @@ def run_fabrication(
push_to_github=push_to_github,
cleanup_local=cleanup_local
)

3 changes: 1 addition & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
anthropic>=0.39.0
google-generativeai>=0.8.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 pyproject.toml still declares anthropic dependency instead of google-generativeai

The migration from Anthropic to Gemini updated requirements.txt (line 1) but did not update pyproject.toml:27, which still declares "anthropic>=0.39.0" as a dependency. Since pyproject.toml is the authoritative dependency specification used by pip install ., pip install -e ., and package builds, anyone installing the package via the standard Python packaging flow will get anthropic installed but not google-generativeai. This causes an immediate ModuleNotFoundError at runtime when import google.generativeai as genai executes in fabricate/generator.py:10.

Prompt for agents
In pyproject.toml, line 27, change the dependency from "anthropic>=0.39.0" to "google-generativeai>=0.8.0" to match the requirements.txt update and the actual import used in fabricate/generator.py.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

PyGithub>=2.1.1
gitpython>=3.1.40
click>=8.1.7
pydantic>=2.5.0
pydantic-settings>=2.1.0
rich>=13.7.0
python-dotenv>=1.0.0