forgecode 0.7.1
A Python library for runtime dynamic code generation and execution using LLMs—generating adaptive Python code from high-level goals, schemas, arguments, and functions.
License: MIT
Requires Python: <4.0,>=3.8.1
Keywords: python, library, development, forgecode

# ForgeCode

ForgeCode is a Python library for runtime dynamic code generation and execution, powered by Large Language Models (LLMs). It provides an abstraction layer that allows developers to specify goals at a high level, without writing concrete implementations. By defining the desired outcome, input arguments, available functions, and output schema, developers can focus on intent rather than manual coding. It enables a new paradigm of code that is generated on the fly at runtime, dynamically adapting to each execution while conforming to the given constraints. 

On the first run, Python code is repeatedly generated by an LLM and executed within an isolated runtime environment, with provided arguments and functions accessible in its global scope, until the code executes successfully without errors and produces output that, when JSON-encoded, conforms precisely to the specified schema. Generated code is cached and efficiently reused for subsequent calls, automatically regenerating only when inputs change or an execution error occurs. Its core strength lies in enabling dynamic, flexible, and adaptive code creation for complex scenarios, such as converting natural language queries into actionable API or SQL calls, or effortlessly transforming complex data structures.

## Installation

```bash
pip install forgecode
```

## Quick Start

```python
from forgecode import ForgeCode, forge

ForgeCode.set_default_llm(OpenAILLMClient(api_key="your-api-key"))
ForgeCode.set_default_model("gpt-4o")

@forge()
def fibonacci(n: int) -> list[int]: 
    """Generate a Fibonacci function"""
    pass

print(fibonacci(10))    # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
```

## Key Features

- **Dynamic code generation**: Code is created on-the-fly based on high-level specifications, such as natural language prompts.
- **Functionality integration**: The code generator can use the provided tools (functions) within the generated code.
- **Code execution**: Code is executed immediately upon call with the provided arguments.
- **Code caching**: Successful code is stored for reuse in subsequent calls.
- **Iterative refinement & validation**: Code is generated, executed, and validated against the provided schema, repeatedly refined until it runs without errors and produces a JSON-encoded response that conforms to the schema.

## Real-World Use Cases

### Natural Language Data Query Interface

Create a dynamic query interface for your APIs:

```python
def query_api(prompt):
    """Convert natural language into API calls"""
    forge = ForgeCode(
        prompt=prompt,
        modules=api_modules, # Dictionary of API functions
    )
    return forge.run()

# Use natural language to query your API
print(query_api("Get all comments for the first post"))
print(query_api("Get all incomplete todos for the user with ID 3"))
```

### Schema Transformation

Easily transform data structures with the builtin schema transformer:

```python
from forgecode.builtins.schema_transform import schema

input_data = [
    {"name": "Alice", "birth_year": 1993},
    {"name": "Bob", "birth_year": 1998},
]

desired_output_example = [
    {"name": "John", "age": 30},
]

# Transform input data to match the desired output format
transformed_data = schema(input_data).to(desired_output_example) 
print(transformed_data) # Output: [{"name": "Alice", "age": 32}, {"name": "Bob", "age": 27}]
```

### Important Details

- ***⚠️ WARNING ⚠️***
  - The code generated by ForgeCode isn't guaranteed to be secure or bug-free, because it's generated by an LLM. Please be cautious.

- **Code caching**: ForgeCode uses a hash-based caching mechanism and regenerates code whenever any of these inputs change:
  - Prompt text
  - Arguments structure/schema (not values)
  - Provided functions
  - Output schema

- **Module idempotency**: Modules provided to ForgeCode must be idempotent (produce the same output when called multiple times with the same input). This is critical because ForgeCode may run these modules multiple times during the iterative code refinement process.
  - Example: providing a function for sending an email can lead to multiple emails being sent if the function is not idempotent.

## Using Decorators

ForgeCode decorators simplify the creation of dynamic, LLM-generated functions. By decorating a function with `@forge`, you can dynamically generate code based on your prompt and specifications.

Here's how you can use decorators:

### Basic Usage

Generate a simple function using a natural language prompt:

```python
from forgecode import forge

@forge(prompt="Generate a Fibonacci function")
def fibonacci(n: int) -> list[int]:
    pass

print(fibonacci(10))
```

### Providing External Tools (Functions)

Supply external functions as tools to assist the code generator:

```python
from forgecode import forge

def formula(a, b):
    return a + b * 2

@forge(prompt="Calculate the number using the provided formula", modules=[formula])
def calculate_num(a: int, b: int) -> int:
    pass

print(calculate_num(5, 10))
```

### Object Transformation

Use decorators to transform objects from one schema to another:

```python
from forgecode import forge

person = {
    "name": "John",
    "lastname": "Doe",
    "year_of_birth": 1990,
    "city": "New York"
}

@forge(prompt="Transform the object to have full_name and age fields")
def transform_object(obj: dict) -> dict:
    pass

print(transform_object(person))
```

### Type Inference with Pydantic (Optional)

Automatically infer schemas from Pydantic models if Pydantic is installed:

```python
from forgecode import forge
from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int

class Greeting(BaseModel):
    message: str

@forge()
def greet_person(person: Person) -> Greeting:
    """Generate a personalized greeting for a person."""
    pass

greeting = greet_person(Person(name="Alice", age=30))

print(greeting.message)
```

### Key points:
- If no prompt is provided explicitly, ForgeCode will use the function’s docstring as a prompt.
- External tools (modules/functions) can be provided for the code generation process.
- ForgeCode automatically infers return schemas from type annotations or provided Pydantic models.

## Advanced Configuration

```python
# Set global defaults
ForgeCode.set_default_llm(OpenAILLMClient(api_key="your-api-key"))
ForgeCode.set_default_model("gpt-4o")
ForgeCode.set_default_max_retries(5)

# Custom execution environment
from forgecode.core.execution_environment.execution_environment import ExecutionEnvironment
ForgeCode.set_default_exec_env(your_custom_environment)

# Custom persistence
from forgecode.core.persistence.code_persistence import CodePersistence
ForgeCode.set_default_code_persistence(your_custom_storage)
```

## Blog

Learn more about ForgeCode in this detailed blog post:
- [ForgeCode: Dynamic Python Code Generation Powered by LLM](https://medium.com/@filipmihajlovicc3/forgecode-dynamic-python-code-generation-powered-by-llm-4e38f7331059)

## License

MIT