Metadata-Version: 2.5
Name: pstone
Version: 0.1.1
Summary: Profit Stone Telemetry SDK — in-process OpenTelemetry instrumentation for AI apps
Project-URL: Homepage, https://profitstone.ai
Project-URL: Repository, https://github.com/simondotpy/profitstone_sdk
Project-URL: Documentation, https://github.com/simondotpy/profitstone_sdk#readme
Project-URL: Issues, https://github.com/simondotpy/profitstone_sdk/issues
Author: Profit Stone
License-Expression: MIT
Keywords: llm,observability,opentelemetry,telemetry
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
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 :: System :: Monitoring
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: opentelemetry-api>=1.27.0
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27.0
Requires-Dist: opentelemetry-sdk>=1.27.0
Requires-Dist: pydantic>=2.0
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.30; extra == 'anthropic'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-benchmark>=4.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.2; extra == 'langchain'
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == 'openai'
Description-Content-Type: text/markdown

# pstone — Profit Stone Telemetry SDK

고객 AI 앱에 설치하는 **in-process** 관측 SDK입니다.  
[OpenTelemetry](https://opentelemetry.io/)로 LLM·도구 호출을 수집해 Profit Stone SaaS **Ingest API**로 보내고, Backend가 Workspace API Key로 `workspace_id`를 붙여 **ClickHouse**에 저장합니다.

Sentry를 붙이듯, 앱에 패키지를 설치하고 `init`에 API Key만 넣으면 됩니다.

```python
from pstone import pstone

pstone.init("psk_live_...")
```

> 상태: **0.1.0 Alpha** — Python MVP. JS(`@pstone/sdk`)는 후속.

설계 문서: [`docs/sdk-design.md`](./docs/sdk-design.md)

---

## 목차

1. [설치](#설치)
2. [빠른 시작](#빠른-시작)
3. [API Key](#api-key)
4. [환경 변수](#환경-변수)
5. [OpenAI / Anthropic 계측](#openai--anthropic-계측)
6. [옵션](#옵션)
7. [수동 Span](#수동-span)
8. [flush / shutdown](#flush--shutdown)
9. [데이터 흐름](#데이터-흐름)
10. [로컬 개발](#로컬-개발)
11. [PyPI 배포](#pypi-배포)

---

## 설치

### 일반 (배포 후)

```bash
pip install pstone
```

OpenAI / Anthropic 계측을 쓰려면 해당 SDK도 앱에 설치되어 있어야 합니다.

```bash
pip install pstone openai anthropic
# 또는 extras (패키지 배포 설정에 따름)
pip install "pstone[openai,anthropic]"
```

### 개발 (이 레포)

```bash
cd profitstone_sdk
uv sync
uv run pytest
```

---

## 빠른 시작

1. Profit Stone Dashboard에서 **Workspace API Key**를 발급합니다.
2. 앱 시작 시점에 `pstone.init`을 **한 번** 호출합니다 (Sentry `init`과 동일).
3. 기존 OpenAI / Anthropic 호출 코드는 그대로 둡니다. `init`이 설치된 라이브러리를 자동 계측합니다.

```python
import os
from openai import OpenAI
from pstone import pstone

# 1) SDK 초기화 (앱 부팅 시 1회)
pstone.init(os.environ["PSTONE_API_KEY"])

# 2) 평소처럼 AI 호출 — Span이 자동 수집되어 Ingest로 전송됨
client = OpenAI()
resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)

# 3) 프로세스 종료 전 (선택) 미전송 Span flush
pstone.flush()
```

Anthropic도 동일합니다.

```python
from anthropic import Anthropic
from pstone import pstone

pstone.init("psk_live_...")

client = Anthropic()
msg = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=256,
    messages=[{"role": "user", "content": "Hello"}],
)
```

---

## API Key

| 항목 | 설명 |
|------|------|
| 발급 위치 | Dashboard → Workspace → API Keys |
| 스코프 | **Workspace** 단위 (`backend-design.md`) |
| 전달 | `pstone.init("psk_...")` 또는 환경 변수 `PSTONE_API_KEY` |
| 서버 처리 | Backend가 키를 검증하고 `workspace_id`를 찾아 ClickHouse에 함께 저장 |

SDK는 `workspace_id`를 직접 보내지 않습니다. **키만** 보내면 됩니다.

```python
from pstone import pstone

# 인자로 전달
pstone.init("psk_live_xxxxxxxx")

# 또는 환경 변수만 설정한 뒤
# export PSTONE_API_KEY=psk_live_xxxxxxxx
pstone.init()
```

키를 소스코드·로그·이슈에 커밋하지 마세요.

---

## 환경 변수

| 변수 | 필수 | 설명 |
|------|------|------|
| `PSTONE_API_KEY` | 권장 | Workspace API Key (`init` 인자 생략 시 사용) |
| `PSTONE_INGEST_URL` | 선택 | Ingest **베이스 URL** (기본 `https://api.profitstone.ai`) |
| `PSTONE_SERVICE_NAME` | 선택 | OTel `service.name` (기본 `pstone-app`) |

실제 전송 경로는 `{PSTONE_INGEST_URL}/v1/ingest/traces` 입니다.

로컬 Backend에 붙일 때:

```bash
export PSTONE_API_KEY=psk_test_...
export PSTONE_INGEST_URL=http://localhost:8000
```

```python
pstone.init(ingest_url="http://localhost:8000")
```

---

## OpenAI / Anthropic 계측

### 자동 (권장)

`pstone.init(..., instrument=True)` (기본값)이면, 프로세스에 `openai` / `anthropic` 패키지가 **설치되어 있을 때** chat / messages API를 패치합니다.

- OpenAI: `chat.completions.create` (및 `parse`가 있으면 함께)
- Anthropic: `messages.create`
- OpenAI-compatible (예: Perplexity 등 `OpenAI` 클라이언트 + 커스텀 `base_url`): OpenAI 계측 경로를 그대로 탑니다.

### 명시적 wrap

글로벌 패치 대신 클라이언트만 감쌀 수 있습니다.

```python
from openai import OpenAI
from anthropic import Anthropic
from pstone import pstone

pstone.init("psk_live_...", instrument=False)

openai_client = pstone.wrap_openai(OpenAI())
anthropic_client = pstone.wrap_anthropic(Anthropic())
```

### 수집되는 주요 속성

OpenTelemetry `gen_ai.*` 계열:

| Attribute | 예 |
|-----------|----|
| `gen_ai.system` | `openai`, `anthropic` |
| `gen_ai.request.model` | `gpt-4o-mini` |
| `gen_ai.prompt` | 메시지 직렬화 (redaction 적용 가능) |
| `gen_ai.completion` | 응답 텍스트 |
| `gen_ai.usage.input_tokens` | 입력 토큰 |
| `gen_ai.usage.output_tokens` | 출력 토큰 |

계측 코드는 **원본 API 예외를 바꾸지 않습니다**. Span 기록 실패는 조용히 무시되어 앱 핫 패스를 보호합니다.

---

## 옵션

```python
pstone.init(
    "psk_live_...",
    ingest_url="https://api.profitstone.ai",
    service_name="my-ai-app",
    capture_content=True,   # 프롬프트·completion 수집
    redact=True,            # 이메일·전화번호 등 기본 PII 마스킹
    instrument=True,        # 자동 패치 on/off
    instrument_openai=True,
    instrument_anthropic=True,
)
```

| 옵션 | 기본 | 설명 |
|------|------|------|
| `capture_content` | `True` | 프롬프트/completion을 Span에 넣을지 |
| `redact` | `True` | 기본 PII 마스킹 |
| `instrument` | `True` | 자동 계측 전체 on/off |
| `instrument_openai` | `True` | OpenAI 패치 |
| `instrument_anthropic` | `True` | Anthropic 패치 |
| `use_batch` | `True` | `BatchSpanProcessor` (테스트에선 `False` 권장) |

민감 프롬프트를 보내지 않으려면:

```python
pstone.init("psk_live_...", capture_content=False)
```

---

## 수동 Span

자동 계측 밖(커스텀 HTTP 등)에서는 헬퍼를 직접 쓸 수 있습니다.

```python
from pstone.span import start_llm_span, record_llm_response

with start_llm_span(
    name="custom.llm",
    system="perplexity",
    model="sonar",
    prompt=[{"role": "user", "content": "hi"}],
) as span:
    # ... 호출 ...
    record_llm_response(
        span,
        completion="...",
        input_tokens=10,
        output_tokens=20,
    )
```

---

## flush / shutdown

| 메서드 | 용도 |
|--------|------|
| `pstone.flush()` | 대기 중인 Span을 Ingest로 강제 전송 |
| `pstone.shutdown()` | flush + 계측 해제 + TracerProvider 종료 |

배치 전송이므로, **짧은 CLI/스크립트**는 종료 전에 `flush()`를 호출하세요.

```python
try:
    # ...
finally:
    pstone.flush()
    pstone.shutdown()
```

---

## 데이터 흐름

```
고객 앱
  pstone.init(API Key)
  → OpenTelemetry Tracer + OTLP/HTTP Exporter
  → AI API 호출 시 Span 기록
  → POST {INGEST_URL}/v1/ingest/traces
       Authorization: Bearer <API Key>

profitstone_backend
  → API Key 검증 → workspace_id 해석
  → ClickHouse에 span + workspace_id 저장
```

SDK는 DB에 직접 붙지 않습니다. HTTPS Ingest만 사용합니다.

---

## 로컬 개발

```bash
cd profitstone_sdk
uv sync
uv run pytest
uv build
```

테스트는 `InMemorySpanExporter`로 Ingest 없이 Span 속성만 검증합니다.

관련 문서:

| 문서 | 경로 |
|------|------|
| SDK 설계 | [`docs/sdk-design.md`](./docs/sdk-design.md) |
| 아키텍처 | `../profitstone_docs/개발문서/architecture.md` |
| Backend | `../profitstone_backend/docs/backend-design.md` |
| 에이전트 맵 | [`AGENTS.md`](./AGENTS.md) |

---

## PyPI 배포

`pip install pstone`이 되려면 PyPI에 업로드해야 합니다.

```bash
# 1) 토큰 설정 (최초 1회)
cp .env.example .env
# .env 에 UV_PUBLISH_TOKEN=pypi-... 입력

# 2) 배포
make publish

# 또는 직접
uv build
uv publish   # UV_PUBLISH_TOKEN 또는 Trusted Publisher
```

성공 직후부터 사용자가 `pip install pstone`을 사용할 수 있습니다.  
절차 상세: [`docs/sdk-design.md`](./docs/sdk-design.md) §13.

---

## 라이선스

MIT
