Conversation
* feat(raw_posts): StarStyle.com adapter (#466) WordPress SSR fashion 사이트(starstyle.com) 어댑터 추가. wots(#465) 인프라 재사용 — discovery_target='raw_posts' 글로벌 피드 모델, PrelabeledData 로 vision 단계 우회. 핵심 차이: per-item 사진 URL 이 없어 thumbnail_url=None → items_thumbnail processor 가 spots bbox 로 hero crop fallback path 사용. brand/title 분리는 보수적으로 product 통째로 (verify 단계 admin 처리). - adapters/_starstyle_html.py: parse_post / parse_sitemap / decode_skimresources (skim affiliate 디코드) - adapters/starstyle.py: thin httpx wrapper, fetch + sitemap discover - scripts/backfill_starstyle_posts.py: sitemap → JSONL streaming → PostgREST bulk INSERT (wots 미러) - service.rs: parse_starstyle_source — slug-sp{ID} 형식 검증 - admin UI: PLATFORM_FILTERS / PLATFORM_TABS / DiscoveryPipelineCard / URL builder - migration: pipeline_settings starstyle row (모든 cycle OFF) - 24 단위 테스트 추가 (parser + adapter + prelabeled round-trip) Closes #466. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(starstyle): force HTTPS on og:image URL (#466) starstyle 의 og:image 가 http:// 로 노출되는데 admin 은 HTTPS 라 mixed-content 로 이미지가 차단되던 문제. CDN 자체는 HTTPS 정상이라 파서 단계에서 https 강제 변환. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#484) starstyle 의 og:image 는 admin (HTTPS) 에서 직접 hotlink 가 막힌다: - HTTPS 요청 → 301 redirect to HTTP → mixed-content 차단 - HTTP 요청 → User-Agent / Referer 가 비어 있으면 403 백필 시점에 boto3 로 upstream 이미지 다운로드 → R2 (RAW_POSTS_R2_BUCKET) 에 ``starstyle/{shard}/{external_id}.jpg`` 키로 업로드하고, raw_posts.image_url 을 R2 public URL 로 저장한다. admin 은 R2 직접 fetch (HTTPS clean, hotlink 없음) → Vercel image-proxy 우회. - HEAD 로 R2 객체 존재 확인 → 재실행 시 중복 업로드 skip (idempotent) - 다운로드/업로드 실패 시 upstream URL 로 fallback (image_url 그대로) - RAW_POSTS_R2_* env 미설정 시 mirror skip + warning - PostData @DataClass(frozen=True) → frozen 제거 (image_url 교체 위해) 검증: 500 row 재백필, 479/500 R2 mirrored. 샘플 R2 URL HTTPS 200 확인. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # packages/ai-server/scripts/backfill_starstyle_posts.py
* release: starstyle R2 mirror fix (#466) (#485) * feat(raw_posts): StarStyle.com adapter (#466) (#481) * feat(raw_posts): StarStyle.com adapter (#466) WordPress SSR fashion 사이트(starstyle.com) 어댑터 추가. wots(#465) 인프라 재사용 — discovery_target='raw_posts' 글로벌 피드 모델, PrelabeledData 로 vision 단계 우회. 핵심 차이: per-item 사진 URL 이 없어 thumbnail_url=None → items_thumbnail processor 가 spots bbox 로 hero crop fallback path 사용. brand/title 분리는 보수적으로 product 통째로 (verify 단계 admin 처리). - adapters/_starstyle_html.py: parse_post / parse_sitemap / decode_skimresources (skim affiliate 디코드) - adapters/starstyle.py: thin httpx wrapper, fetch + sitemap discover - scripts/backfill_starstyle_posts.py: sitemap → JSONL streaming → PostgREST bulk INSERT (wots 미러) - service.rs: parse_starstyle_source — slug-sp{ID} 형식 검증 - admin UI: PLATFORM_FILTERS / PLATFORM_TABS / DiscoveryPipelineCard / URL builder - migration: pipeline_settings starstyle row (모든 cycle OFF) - 24 단위 테스트 추가 (parser + adapter + prelabeled round-trip) Closes #466. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(starstyle): force HTTPS on og:image URL (#466) starstyle 의 og:image 가 http:// 로 노출되는데 admin 은 HTTPS 라 mixed-content 로 이미지가 차단되던 문제. CDN 자체는 HTTPS 정상이라 파서 단계에서 https 강제 변환. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: retrigger CI with bump label * fix(starstyle-backfill): mirror hero images to R2 before INSERT (#466) (#484) starstyle 의 og:image 는 admin (HTTPS) 에서 직접 hotlink 가 막힌다: - HTTPS 요청 → 301 redirect to HTTP → mixed-content 차단 - HTTP 요청 → User-Agent / Referer 가 비어 있으면 403 백필 시점에 boto3 로 upstream 이미지 다운로드 → R2 (RAW_POSTS_R2_BUCKET) 에 ``starstyle/{shard}/{external_id}.jpg`` 키로 업로드하고, raw_posts.image_url 을 R2 public URL 로 저장한다. admin 은 R2 직접 fetch (HTTPS clean, hotlink 없음) → Vercel image-proxy 우회. - HEAD 로 R2 객체 존재 확인 → 재실행 시 중복 업로드 skip (idempotent) - 다운로드/업로드 실패 시 upstream URL 로 fallback (image_url 그대로) - RAW_POSTS_R2_* env 미설정 시 mirror skip + warning - PostData @DataClass(frozen=True) → frozen 제거 (image_url 교체 위해) 검증: 500 row 재백필, 479/500 R2 mirrored. 샘플 R2 URL HTTPS 200 확인. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(release): backend bump ai=1.5.1 api=0.10.1 [skip ci] * feat(raw_posts): R2 cleanup on raw_post delete (#466) raw_post 삭제 시 image_url 이 raw_posts R2 객체이고 verified posts 가 같은 URL 을 참조하지 않으면 R2 객체도 같이 삭제. verify 후에는 posts. image_url 가 raw_post.image_url 그대로 복사되므로 (#333) — verified post 가 참조 중이면 보존해야 깨지지 않는다. 또한 storage 명명을 db 패턴(db / assets_db) 에 맞춤: - storage_client → operation_storage - 신규 RAW_POSTS_R2_* → assets_storage - AppConfig.storage / assets_storage, AppState.operation_storage / assets_storage 일관 매핑. config: - StorageConfig 두 개 (storage / assets_storage), 별도 R2 버킷 wiring - RAW_POSTS_R2_{ACCOUNT_ID, ACCESS_KEY_ID, SECRET_ACCESS_KEY, BUCKET, PUBLIC_URL} service::delete_item: - 시그니처 (assets_db, prod_db, storage, public_url, id) — 4개의 DI - raw_post.image_url 추출 → DELETE 진행 - extract_assets_r2_key 로 R2 key 추출 (public_url prefix strip) - posts.image_url COUNT — 0 이면 storage.delete(key) 호출 - 실패는 best-effort warn (이미 raw_post 삭제는 성공) 테스트: 4개 단위 (key 추출 - prefix/trailing slash/other domain/empty url). solutions/tests.rs 의 AdminSolutionListQuery 빌드에 has_url=None 추가 (unrelated 사전 컴파일 에러 — 본 PR 의 cargo test --lib 통과 위해 보강). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: decoded-ci <ci@decodedcorp.com>
Store the judged image URL with vision filter audit rows so Instagram skip decisions can still render admin thumbnails after raw_posts cleanup. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
작업 1 (마이그레이션, 1080 row) 후속 — 신규 verify 데이터도 동일한 single-source
컨벤션 (r2.decoded.style/posts/{post_id} / r2.decoded.style/items/{solution_id})
유지하도록 verify_raw_post 흐름 수정.
기존: raw_post.image_url (assets R2 dev pub URL) 그대로 posts.image_url 복사
→ 운영 DB 가 dev 도메인 URL 들고 있는 버그.
새 흐름:
1. prod_txn 진입 전 R2 copy:
- post_id 사전 생성 → assets_storage.download(raw_key) →
operation_storage.upload("posts/{post_id}")
- 각 visible item: solution_id 사전 생성 → items thumbnail 동일 패턴
2. prod_txn: create_post_from_raw / create_solution_for_verify 에 미리 생성한
UUID + 새 operation URL 전달 (시그니처에 명시 인자 추가)
3. assets_txn: raw_post.image_url 도 operation URL 로 갱신 (단일 진실 소스)
4. 두 트랜잭션 commit 후 raw 객체 best-effort delete (warn 만, orphan 허용)
코드 변경:
- StorageClient trait 에 `download(key) -> (bytes, Option<content_type>)` 추가
- CloudflareR2Client GetObject 구현, NotFound 매핑
- DummyStorageClient stub 구현
- 신규 `raw_posts/relocate.rs` — relocate_raw_to_operation helper + 4 단위 테스트
- extract_assets_r2_key → pub(crate) 격상 (relocate 가 재사용)
- create_post_from_raw 시그니처: post_id, image_url 명시 인자 (내부 Uuid::new_v4 제거)
- create_solution_for_verify 시그니처: solution_id 명시 인자
- verify_raw_post: prod_txn 전 R2 copy 단계 + raw cleanup post-commit
실패 처리:
- copy 도중 실패 → BadRequest/NotFound/ExternalService Err, prod_txn 미시작 (DB 부작용 0)
- 부분 성공 (hero copy OK / item N copy fail) → op bucket 에 hero orphan
잔존 (lifecycle rule / 별도 GC out of scope)
- raw delete 실패 → warn 로깅만, verify 자체는 성공 유지
테스트:
- 4 단위 (relocate happy/fallback/non-raw-rejection/not-found-propagation)
- 24 회귀 통과 (parse_source_identifier_*, verify_*)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#494) Wiki lint 가 docs/wiki/schema/tags.md 에 등록 안 된 태그 거부. api / ui 만 남겨 dev→main 머지 차단 해제. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # packages/api-server/src/domains/raw_posts/service.rs
…ard (#496) * feat(cost-tracking): Gemini API per-call cost tracking + admin dashboard ai-server 의 Gemini 호출 (raw_post 6 + editorial 7) 모두를 호출 시점에 DB 적립 + 어드민 대시보드에서 일/step/model/pipeline 별 spend 가시화. 핵심 결정: - 단가는 코드 하드코딩 없음 — `gemini_pricing` DB 테이블 SOT (SCD-2, effective_from/to). 어드민 UI 에서 inline upsert. - per-call `pricing_snapshot` 보존 — 단가 변경 후에도 과거 cost 재현 가능. - fire-and-forget recorder — 추적이 본 파이프라인 latency/실패에 영향 0. - 실패도 row 적립 (ok=false, error_class) — 안전·정책 차단, quota, parse_error, timeout, no_pricing 등 가시화. - 5분 in-process pricing cache — 어드민 변경 후 최악 5분 stale 허용. DB (`supabase-assets/migrations/20260514140000_gemini_cost_tracking.sql`): - gemini_pricing (SCD-2, 12 seed row — Pro / Flash / Flash-Lite / Flash-Image / grounding) - gemini_usage_events (per-call, BRIN + composite index 시간순) - gemini_spend_daily (view, 대시보드 read-side) ai-server (`src/services/cost_tracking/`): - `track_call(step, model, extract, coro)` — coroutine wrapper. usage_metadata 자동 추출 + estimator (token × DB 단가 × cached 우대) + recorder INSERT. - raw_post scheduler / post_editorial service 에서 `set_context()` 1회 호출 → 이후 모든 Gemini 호출이 raw_post_id / post_id / pipeline 자동 적립. - 13 call site wrap: items/subject/spots/nano_banana(hero+thumbnail)/url_grounded/url_filter (raw_post 6), design_spec/image_analysis/item_search/news_research/editorial/review/celeb_search (editorial 7). api-server (`src/domains/admin/gemini_cost.rs`): - GET /spend/{daily,by-step,by-model,by-pipeline,today,top-raw-posts} - GET / POST /pricing (POST 가 기존 active row close + 새 row insert TX) - DELETE /pricing/{id} (retire) web (`/admin/gemini-cost`): - KPI row (Today / Yesterday / 7d / 30d) - Daily spend AreaChart (7/14/30d period selector) - Spend by step / model / pipeline (가로 Bar) - Top expensive raw_posts (drill-down → /admin/raw-posts/[id]) - Pricing editor (inline upsert + history toggle + retire) - /admin home 에 `<GeminiCostMini>` 카드 + sidebar 새 "Observability" 그룹 검증: - migration syntax check (local DB BEGIN; ... ROLLBACK;) - e2e unit test (scripts/cost_tracking_e2e_test.py) — Pro/Flash/Image/cached/ failure/no-pricing 6 case 모두 단가 계산 정확 (1000 in × \$1.25/M + 500 out × \$10/M = \$0.006250 등 모두 ✓) - 실측 (scripts/cost_probe.py) — 운영 raw_post 1건 풀 파이프라인: fixed \$0.041 + per-item \$0.075 = 3 items \$0.268, 5 items 추정 ~\$0.42 - ai-server import / api-server cargo check / web typecheck + lint 모두 clean Out of scope (별도 트랙): - Cloud Billing Catalog API 자동 sync (Vertex 이전 후) - daily threshold alert (Slack/텔레그램) - 비용 hot path 최적화 (thumbnail/grounded 감축) — 데이터 적립 후 결정 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(cost-tracking): real-parser wrap verification script 기존 cost_tracking_e2e_test.py 는 `track_call` 을 직접 호출 (mock coroutine). 이 스크립트는 실제 `SubjectParser` 클래스를 import + 호출 → 그 내부에 박힌 wrap 코드 라인이 작동해서 DB row 가 적립되는지 검증한다. 검증 결과 (운영 Pinterest 이미지 1장, ~$0.0005): step=subject_parser, model=gemini-2.5-flash, pipeline=raw_post, prompt_tokens=813, completion_tokens=98, cost_usd=$0.000489, pricing_snapshot={input_token: 0.0000003, output_token: 0.0000025} → wrap 코드 경로 + context 자동 전파 + 단가 cache lookup + INSERT 모두 실제로 작동함을 확인. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Release dev → main. Includes:
Highlights
gemini_pricingDB 테이블 SOT (SCD-2)/admin/gemini-cost대시보드 — KPI / Daily chart / step·model·pipeline 별 / Top expensive raw_posts / Pricing editorTest plan
gemini_usage_events적립 확인🤖 Generated with Claude Code