Metadata-Version: 2.4
Name: pylego-blocks
Version: 0.1.13
Summary: Composable block-based Python component architecture framework
Project-URL: Homepage, https://github.com/hoksi/pylego
Project-URL: Repository, https://github.com/hoksi/pylego
Project-URL: Documentation, https://hoksi.github.io/pylego
Project-URL: Bug Tracker, https://github.com/hoksi/pylego/issues
Project-URL: Changelog, https://github.com/hoksi/pylego/blob/main/CHANGELOG.md
Author-email: hoksi <hoksi@forbiz.co.kr>
License: MIT
Keywords: architecture,async,block,composable,dag,framework,pipeline,plugin,protocol,pydantic
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: pydantic>=2.9
Requires-Dist: pyyaml>=6.0
Provides-Extra: all
Requires-Dist: aiomysql>=0.2; extra == 'all'
Requires-Dist: aiosmtplib>=3.0; extra == 'all'
Requires-Dist: aiosqlite>=0.20; extra == 'all'
Requires-Dist: anthropic>=0.25; extra == 'all'
Requires-Dist: apscheduler>=3.10; extra == 'all'
Requires-Dist: duckdb>=0.10; extra == 'all'
Requires-Dist: duckduckgo-search>=6.0; extra == 'all'
Requires-Dist: fastapi>=0.110; extra == 'all'
Requires-Dist: httpx>=0.27; extra == 'all'
Requires-Dist: jinja2>=3.0; extra == 'all'
Requires-Dist: openai>=1.0; extra == 'all'
Requires-Dist: openpyxl>=3.0; extra == 'all'
Requires-Dist: opentelemetry-api>=1.20; extra == 'all'
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.20; extra == 'all'
Requires-Dist: opentelemetry-sdk>=1.20; extra == 'all'
Requires-Dist: pypdf>=4.0; extra == 'all'
Requires-Dist: redis>=5.0; extra == 'all'
Requires-Dist: reportlab>=4.0; extra == 'all'
Requires-Dist: uvicorn[standard]>=0.29; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.25; extra == 'anthropic'
Provides-Extra: chromadb
Requires-Dist: chromadb>=0.5; extra == 'chromadb'
Provides-Extra: discord
Requires-Dist: httpx>=0.27; extra == 'discord'
Provides-Extra: duckdb
Requires-Dist: duckdb>=0.10; extra == 'duckdb'
Provides-Extra: duckduckgo
Requires-Dist: duckduckgo-search>=6.0; extra == 'duckduckgo'
Provides-Extra: excel
Requires-Dist: openpyxl>=3.0; extra == 'excel'
Provides-Extra: gmail
Requires-Dist: aiosmtplib>=3.0; extra == 'gmail'
Provides-Extra: google-search
Requires-Dist: httpx>=0.27; extra == 'google-search'
Provides-Extra: google-translate
Requires-Dist: httpx>=0.27; extra == 'google-translate'
Provides-Extra: http
Requires-Dist: httpx>=0.27; extra == 'http'
Provides-Extra: jinja2
Requires-Dist: jinja2>=3.0; extra == 'jinja2'
Provides-Extra: mysql
Requires-Dist: aiomysql>=0.2; extra == 'mysql'
Provides-Extra: ollama
Requires-Dist: httpx>=0.27; extra == 'ollama'
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == 'openai'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.20; extra == 'otel'
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.20; extra == 'otel'
Requires-Dist: opentelemetry-sdk>=1.20; extra == 'otel'
Provides-Extra: pdf
Requires-Dist: pypdf>=4.0; extra == 'pdf'
Requires-Dist: reportlab>=4.0; extra == 'pdf'
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == 'redis'
Provides-Extra: scheduler
Requires-Dist: apscheduler>=3.10; extra == 'scheduler'
Provides-Extra: serve
Requires-Dist: fastapi>=0.110; extra == 'serve'
Requires-Dist: uvicorn[standard]>=0.29; extra == 'serve'
Provides-Extra: slack
Requires-Dist: httpx>=0.27; extra == 'slack'
Provides-Extra: sqlite
Requires-Dist: aiosqlite>=0.20; extra == 'sqlite'
Provides-Extra: telegram
Requires-Dist: httpx>=0.27; extra == 'telegram'
Description-Content-Type: text/markdown

# pylego

> 레고처럼 조립 가능한 파이썬 컴포넌트 아키텍처 프레임워크.
> 작은 표준 블록(Block)을 pydantic v2 계약(Stud)으로 스냅해 Assembly로 조립한다.

## 설치

```bash
pip install pylego-blocks
```

uv 사용 시:

```bash
uv add pylego-blocks
```

선택적 의존성 설치:

```bash
pip install pylego-blocks[anthropic]   # Claude API
pip install pylego-blocks[openai]      # OpenAI API
pip install pylego-blocks[serve]       # FastAPI HTTP 서빙
pip install pylego-blocks[all]         # 전체
```

---

## 빠른 시작

### 1. Block 정의

```python
from pylego_blocks.core.block import Block
from pylego_blocks.core.stud import Stud


class TextInput(Stud):
    text: str


class UpperOutput(Stud):
    text: str


class LengthOutput(Stud):
    length: int


class UpperBlock(Block[TextInput, UpperOutput]):
    input_model = TextInput
    output_model = UpperOutput

    async def run(self, inp: TextInput) -> UpperOutput:
        return UpperOutput(text=inp.text.upper())


class LengthBlock(Block[UpperOutput, LengthOutput]):
    input_model = UpperOutput
    output_model = LengthOutput

    async def run(self, inp: UpperOutput) -> LengthOutput:
        return LengthOutput(length=len(inp.text))
```

### 2. Assembly 조립 및 실행

```python
from pylego_blocks.core.assembly import Assembly

# 조립 시점에 Stud 계약 불일치를 즉시 검출
pipeline = Assembly([UpperBlock(), LengthBlock()])

# 비동기
import asyncio
result = asyncio.run(pipeline.run(TextInput(text="hello")))

# 동기 편의 API
result = pipeline.run_sync(TextInput(text="hello"))
print(result.length)  # 5
```

### 3. YAML 선언형 조립

```yaml
# pipeline.yaml
name: text-pipeline
blocks:
  - class: myapp.blocks.UpperBlock
  - class: myapp.blocks.LengthBlock
```

```python
from pylego_blocks.declarative import load_assembly

pipeline = load_assembly("pipeline.yaml")
result = pipeline.run_sync(TextInput(text="hello"))
```

### 4. FanOut — 동시 분기 실행

```python
from pylego_blocks.core.fanout import FanOut, Gathered

fan = FanOut([UpperBlock(), LengthBlock()])
# 두 브랜치를 asyncio.gather 로 동시 실행 → Gathered(results=(…, …))
result: Gathered = asyncio.run(fan.run(TextInput(text="hello")))
```

### 5. BlockRegistry

```python
from pylego_blocks.registry import register, default_registry

@register
class UpperBlock(Block[TextInput, UpperOutput]):
    ...

cls = default_registry.get("UpperBlock")
```

---

## 핵심 원칙

1. 블록은 다른 블록 내부를 모른다 — Stud(계약)만 안다.
2. 블록은 단독 실행·단독 테스트가 가능해야 한다.
3. Assembly는 블록과 구분 불가능해야 한다 (재귀적 합성).
4. 런타임 교체(sync↔async)가 블록 수정 없이 가능해야 한다.

---

## 개발 환경

```bash
# 의존성 설치
uv sync

# 테스트
uv run pytest

# 타입 체크
uv run mypy src/

# 빌드
uv build
```

## 문서

- [로드맵](docs/ROADMAP.md)
- [CHANGELOG](CHANGELOG.md)
