pstone — Profit Stone Telemetry SDK
고객 AI 앱에 설치하는 in-process 관측 SDK입니다.
OpenTelemetry로 LLM·도구 호출을 수집해 Profit Stone SaaS Ingest API로 보내고, Backend가 Workspace API Key로 workspace_id를 붙여 ClickHouse에 저장합니다.
Sentry를 붙이듯, 앱에 패키지를 설치하고 init에 API Key만 넣으면 됩니다.
from pstone import pstone
pstone.init("psk_live_...")
상태: 0.1.2 Alpha — Python MVP (
python/). JS(@profitstone/pstone)·Java는 scaffold.
설계 문서: docs/sdk-design.md
목차
설치
일반 (배포 후)
pip install pstone
OpenAI / Anthropic 계측을 쓰려면 해당 SDK도 앱에 설치되어 있어야 합니다.
pip install pstone openai anthropic
# 또는 extras (패키지 배포 설정에 따름)
pip install "pstone[openai,anthropic]"
개발 (이 레포)
cd profitstone_sdk
make python-sync
make python-test
또는 python/ 디렉터리에서:
cd python
uv sync
uv run pytest
빠른 시작
- Profit Stone Dashboard에서 Workspace API Key를 발급합니다.
- 앱 시작 시점에
pstone.init을 한 번 호출합니다 (Sentryinit과 동일). - 기존 OpenAI / Anthropic 호출 코드는 그대로 둡니다.
init이 설치된 라이브러리를 자동 계측합니다.
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도 동일합니다.
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를 직접 보내지 않습니다. 키만 보내면 됩니다.
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에 붙일 때:
export PSTONE_API_KEY=psk_test_...
export PSTONE_INGEST_URL=http://localhost:8000
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
글로벌 패치 대신 클라이언트만 감쌀 수 있습니다.
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 기록 실패는 조용히 무시되어 앱 핫 패스를 보호합니다.
옵션
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 권장) |
민감 프롬프트를 보내지 않으려면:
pstone.init("psk_live_...", capture_content=False)
수동 Span
자동 계측 밖(커스텀 HTTP 등)에서는 헬퍼를 직접 쓸 수 있습니다.
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()를 호출하세요.
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만 사용합니다.
로컬 개발
# 레포 루트
make python-sync
make python-test
make python-build
# 또는 python/ 에서
cd python
uv sync
uv run pytest
uv build
테스트는 InMemorySpanExporter로 Ingest 없이 Span 속성만 검증합니다.
관련 문서:
| 문서 | 경로 |
|---|---|
| SDK 설계 | ../docs/sdk-design.md |
| 아키텍처 | ../../profitstone_docs/개발문서/architecture.md |
| Backend | ../../profitstone_backend/docs/backend-design.md |
| 에이전트 맵 | ../AGENTS.md |
PyPI 배포
pip install pstone이 되려면 PyPI에 업로드해야 합니다.
# 1) 토큰 설정 (레포 루트, 최초 1회)
cp .env.example .env
# .env 에 UV_PUBLISH_TOKEN=pypi-... 입력
# 2) 배포 (Python만 — JS/Java와 독립)
make python-publish
# 또는 python/ 에서
cd python && make publish
성공 직후부터 사용자가 pip install pstone을 사용할 수 있습니다.
절차 상세: ../docs/sdk-design.md §13.
라이선스
MIT
Release files for pstone 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pstone-0.1.3.tar.gz | 117.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pstone-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 135.1 kB
Release files / pstone-0.1.3.tar.gz
| Download URL | pstone-0.1.3.tar.gz |
|---|---|
| Size | 117.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
627f9407df83a71a57554422fe63848ae5940a81686fa2ac2812075a43e1ab2e
|
|
BLAKE2b-256 checksum How to use checksums |
5b7eac5198cf03a0a94fedf9c3e70f91d564bad8761d1b90aafcc970b477323a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / pstone-0.1.3-py3-none-any.whl
| Download URL | pstone-0.1.3-py3-none-any.whl |
|---|---|
| Size | 17.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f0d051db716ed1d62569aa06f7898f255b187cf1ab2ce2da02140f76425754b2
|
|
BLAKE2b-256 checksum How to use checksums |
105dbea9837e4341f57521cf155dd32d6ec1e2d74f2ec3959d5ec35f08c3cb23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|