Skip to content

Latest commit

 

History

History

README.md

CodeUChain Python: Comprehensive Implementation

CodeUChain provides a powerful framework for chaining processing links with hook support and flexible states.

📦 Installation

pip install codeuchain

Zero external dependencies - pure Python!

🤖 LLM Support

This package supports the llm.txt standard for easy AI/LLM integration. See llm-full.txt for comprehensive documentation.

Features

  • State: Immutable by default, mutable for flexibility—embracing Python's dynamism.
  • Link: Selfless processors, async and ecosystem-rich.
  • Chain: Harmonious connectors with conditional flows.
  • Hook: Gentle enhancers, optional and forgiving.
  • Error Handling: Compassionate routing and retries.
  • Typed Features: Optional static typing with TypedDict and generics for type safety.

Quick Start

import asyncio
from codeuchain import State, Chain, MathLink, LoggingHook

async def main():
    chain = Chain()
    chain.add_link("math", MathLink("sum"))
    chain.use_hook(LoggingHook())
    
    ctx = State({"numbers": [1, 2, 3]})
    result = await chain.run(ctx)
    print(result.get("result"))  # 6

asyncio.run(main())

Typed Features (Optional)

CodeUChain supports optional static typing for enhanced type safety and better IDE support:

Basic Typed Usage

from typing import TypedDict
from codeuchain import State, Link, Chain

class InputData(TypedDict):
    numbers: list[int]
    operation: str

class OutputData(InputData):
    result: float

class SumLink(Link[InputData, OutputData]):
    async def call(self, ctx: State[InputData]) -> State[OutputData]:
        numbers = ctx.get("numbers") or []
        total = sum(numbers)
        return ctx.insert_as("result", float(total))

# Usage
async def main():
    chain: Chain[InputData, OutputData] = Chain()
    chain.add_link(SumLink(), "sum")
    
    data: InputData = {"numbers": [1, 2, 3], "operation": "sum"}
    ctx: State[InputData] = State(data)
    
    result: State[OutputData] = await chain.run(ctx)
    print(result.get("result"))  # 6.0

asyncio.run(main())

Type Evolution with insert_as()

The insert_as() method enables clean type evolution without casting:

class UserInput(TypedDict):
    name: str
    email: str

class UserWithProfile(TypedDict):
    name: str
    email: str
    age: int
    preferences: dict

# Clean type evolution
ctx = State[UserInput]({"name": "Alice", "email": "alice@example.com"})
evolved_ctx = (
    ctx
    .insert_as("age", 30)
    .insert_as("preferences", {"theme": "dark"})
)

Choosing Between Typed and Untyped

Use Untyped (Default):

  • Prototyping and exploration
  • Dynamic data structures
  • Simple scripts
  • Maximum flexibility

Use Typed (Optional):

  • Production systems
  • Complex workflows
  • Team collaboration
  • Long-term maintenance
  • Enhanced IDE support

Both approaches work together—you can mix typed and untyped components in the same chain!

HTTP Examples

Need HTTP functionality? See examples/http_examples/ for implementations:

Built-in HTTP (Zero Dependencies)

# Copy from examples/http_examples/http_links.py
from your_project.simple_http import SimpleHttpLink
link = SimpleHttpLink("https://api.example.com/data")

Advanced HTTP (aiohttp)

# Requires: pip install aiohttp
from your_project.aio_http import AioHttpLink
link = AioHttpLink("https://api.example.com/data", method="POST")

Examples

See the examples/ directory for comprehensive demonstrations:

  • typed_vs_untyped_comparison.py - Side-by-side comparison of approaches
  • typed_workflow_patterns.py - Common patterns for typed workflows
  • insert_as_method_demo.py - Type evolution demonstrations
  • simple_math.py - Basic untyped usage

Design Approach

Optimized for Python's strengths—dynamic, ecosystem-integrated, and developer-friendly. Start fresh, build powerful processing pipelines.