Metadata-Version: 2.4
Name: algocean-codex-oauth
Version: 0.1.0
Summary: Drop-in ChatOpenAI replacement for LangChain/LangGraph using Codex ChatGPT OAuth subscription.
Project-URL: Homepage, https://github.com/algocean1204/AlgoceanCodexOAuth
Project-URL: Documentation, https://github.com/algocean1204/AlgoceanCodexOAuth#readme
Project-URL: Repository, https://github.com/algocean1204/AlgoceanCodexOAuth
Project-URL: Issues, https://github.com/algocean1204/AlgoceanCodexOAuth/issues
Project-URL: Changelog, https://github.com/algocean1204/AlgoceanCodexOAuth/releases
Author: algocean1204
License-Expression: MIT
License-File: LICENSE
Keywords: algocean,chatgpt,codex,langchain,langgraph,oauth,openai
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: langchain-core>=0.3.0
Provides-Extra: dev
Requires-Dist: pydantic>=2.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# AlgoceanCodexOAuth

LangChain / LangGraph에서 **`ChatOpenAI` 자리에 그대로 꽂는** Codex ChatGPT OAuth LLM 래퍼입니다.

API key 과금 경로는 코드에서 차단하고, 로컬 `codex login`으로 저장된 **ChatGPT OAuth 구독 한도**만 사용합니다.

```text
LangGraph / LangChain
  → AlgoceanCodexOAuth
  → codex exec
  → 로컬 ChatGPT OAuth 세션
  → Codex 구독 한도 / 크레딧
```

## 설치

### 1. Codex CLI + ChatGPT OAuth (1회)

```bash
npm install -g @openai/codex

unset OPENAI_API_KEY
unset CODEX_API_KEY
codex logout
codex login
codex login status
```

공식 인증 문서: [Codex Authentication](https://developers.openai.com/codex/auth)

### 2. Python 패키지

```bash
pip install algocean-codex-oauth
```

LangGraph 프로젝트:

```bash
pip install algocean-codex-oauth langgraph langchain-core
```

## Quick Start

### ChatOpenAI → AlgoceanCodexOAuth (1:1 교체)

**Before (API key)**

```python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

llm = ChatOpenAI(model="gpt-4o")
response = llm.invoke([HumanMessage(content="FastAPI Depends를 짧게 설명해줘.")])
print(response.content)
```

**After (Codex OAuth)**

```python
from algocean_codex_oauth import AlgoceanCodexOAuth
from langchain_core.messages import HumanMessage

llm = AlgoceanCodexOAuth(model="gpt-5.5")
response = llm.invoke([HumanMessage(content="FastAPI Depends를 짧게 설명해줘.")])
print(response.content)
```

## LangGraph

### 단일 노드

```python
from langchain_core.messages import HumanMessage, SystemMessage
from langgraph.graph import StateGraph, END
from typing_extensions import TypedDict

from algocean_codex_oauth import AlgoceanCodexOAuth

class State(TypedDict):
    user_input: str
    answer: str

llm = AlgoceanCodexOAuth(model="gpt-5.5")

async def assistant_node(state: State) -> State:
    messages = [
        SystemMessage(content="간결한 개인 비서."),
        HumanMessage(content=state["user_input"]),
    ]
    ai = await llm.ainvoke(messages)
    return {"answer": ai.content}

graph = StateGraph(State)
graph.add_node("assistant", assistant_node)
graph.set_entry_point("assistant")
graph.add_edge("assistant", END)
app = graph.compile()
```

### ReAct Agent

```python
from langgraph.prebuilt import create_react_agent
from langchain_core.messages import HumanMessage
from algocean_codex_oauth import AlgoceanCodexOAuth

llm = AlgoceanCodexOAuth(model="gpt-5.5")
agent = create_react_agent(llm, tools=[])
result = agent.invoke({"messages": [HumanMessage(content="hello")]})
```

### Structured Output (LangGraph state merge)

```python
from pydantic import BaseModel, Field
from langchain_core.messages import HumanMessage
from algocean_codex_oauth import AlgoceanCodexOAuth

class Analysis(BaseModel):
    summary: str = Field(description="요약")
    risk_level: str = Field(description="low | medium | high")

llm = AlgoceanCodexOAuth(model="gpt-5.5")
structured = llm.with_structured_output(Analysis)
result = structured.invoke([HumanMessage(content="위험도를 평가해줘.")])
print(result.summary, result.risk_level)
```

내부적으로 Codex CLI `--output-schema`를 사용합니다.  
문서: [Codex non-interactive mode](https://developers.openai.com/codex/noninteractive)

## ChatOpenAI 기능 대응

| ChatOpenAI (API key) | AlgoceanCodexOAuth |
| --- | --- |
| `llm.invoke(messages)` | 동일 |
| `await llm.ainvoke(messages)` | 동일 |
| `llm.astream(messages)` | 동일 |
| `llm.with_structured_output(schema)` | 동일 |
| LangGraph `state["messages"]` 멀티턴 | `thread_mode="messages"` (기본) |
| 대화 세션 유지 | `thread_mode="codex_resume"` + `ephemeral=False` |
| 새 대화 시작 | `llm.reset_thread()` |
| `response.response_metadata["usage"]` | 동일 |

## 생성자

```python
AlgoceanCodexOAuth(
    model: str = "gpt-5.5",
    *,
    timeout: int = 180,
    sandbox: str = "read-only",
    workdir: str | None = None,
    ephemeral: bool = True,
    thread_mode: str = "messages",     # messages | codex_resume
    codex_bin: str = "codex",
    require_chatgpt_login: bool = True,
)
```

### Preset

```python
llm = AlgoceanCodexOAuth.chat(model="gpt-5.5")                  # messages 멀티턴
llm = AlgoceanCodexOAuth.repo_read(workdir="/path/to/repo")
llm = AlgoceanCodexOAuth.repo_write(workdir="/path/to/repo")    # codex_resume
```

| Preset | thread_mode | 멀티턴 방식 |
| --- | --- | --- |
| `chat()` | `messages` | ChatOpenAI처럼 messages 히스토리 전달 |
| `repo_read(path)` | `messages` | messages 히스토리 |
| `repo_write(path)` | `codex_resume` | Codex exec resume (에이전트) |

## 멀티턴 — ChatOpenAI와 동일하게

### 방식 1: messages (기본, LangGraph 표준)

```python
messages = [HumanMessage(content="코드네임은 ALPHA7")]
ai1 = await llm.ainvoke(messages)
messages += [ai1, HumanMessage(content="코드네임이 뭐야?")]
ai2 = await llm.ainvoke(messages)
```

LangGraph `state["messages"]` 패턴과 1:1 동일합니다.

### 방식 2: codex_resume (Codex thread 유지)

```python
llm = AlgoceanCodexOAuth(
    model="gpt-5.5",
    workdir="/path/to/repo",
    sandbox="read-only",
    ephemeral=False,
    thread_mode="codex_resume",
)

await llm.ainvoke([HumanMessage(content="첫 질문")])
await llm.ainvoke([HumanMessage(content="이어서")])  # exec resume
llm.reset_thread()  # 새 대화
```

### Streaming

```python
async for chunk in llm.astream([HumanMessage(content="hello")]):
    print(chunk.content, end="", flush=True)
```

## Provider 스위치 (미니 Codex)

```python
def get_llm(use_codex_oauth: bool):
    if use_codex_oauth:
        from algocean_codex_oauth import AlgoceanCodexOAuth
        return AlgoceanCodexOAuth(model="gpt-5.5")
    from langchain_openai import ChatOpenAI
    return ChatOpenAI(model="gpt-4o")
```

그래프 코드는 동일하고 import / 클래스만 바꾸면 됩니다.

## OAuth 정책

라이브러리는 호출마다 아래를 강제합니다.

- `OPENAI_API_KEY`, `CODEX_API_KEY` 환경 변수 제거
- `require_chatgpt_login=True` 시 `codex login status`로 ChatGPT OAuth 확인
- API key 인증 감지 시 `AlgoceanCodexOAuthError` 발생

## 아키텍처

```text
algocean_codex_oauth/
├── chat_model.py    # AlgoceanCodexOAuth (BaseChatModel) — public API
├── client.py        # codex exec / exec resume
├── auth.py          # OAuth 검증, API key 차단
├── config.py        # AlgoceanCodexConfig
├── messages.py      # LangChain messages → prompt
├── session.py       # 멀티턴 thread resume (고급)
└── errors.py
```

## 멀티턴 (Session 래퍼)

`AlgoceanCodexSession`은 `thread_mode="codex_resume"` 편의 래퍼입니다.  
직접 `AlgoceanCodexOAuth(..., thread_mode="codex_resume")` 를 써도 동일합니다.

## 제한

- **개인 로컬 / 개인 구독** 용도입니다. SaaS 서버에서 외부 사용자에게 AI를 제공하는 용도에는 맞지 않습니다.
- Codex CLI(`codex`)가 PATH에 있어야 합니다.
- `danger-full-access` sandbox는 격리 환경에서만 사용하세요.

## 개발

```bash
git clone https://github.com/algocean1204/AlgoceanCodexOAuth.git
cd AlgoceanCodexOAuth
pip install -e ".[dev]"
pytest
```

## PyPI

```bash
pip install algocean-codex-oauth
```

## License

MIT

## Links

- GitHub: https://github.com/algocean1204/AlgoceanCodexOAuth
- Codex CLI: https://developers.openai.com/codex/cli/reference
- Codex Auth: https://developers.openai.com/codex/auth
