Skip to content

[Feat] Discord 웹훅 로그 상세화 — 로드맵 생성/수정 파이프라인 내부 추적#70

Merged
zweadfx merged 10 commits intomainfrom
feat/webhook-pipeline-tracking
Apr 8, 2026
Merged

[Feat] Discord 웹훅 로그 상세화 — 로드맵 생성/수정 파이프라인 내부 추적#70
zweadfx merged 10 commits intomainfrom
feat/webhook-pipeline-tracking

Conversation

@zweadfx
Copy link
Copy Markdown
Contributor

@zweadfx zweadfx commented Apr 8, 2026

1. 개요

2. 작업 내용

  • append_job_log에 level 파라미터 추가하여 info/detail 구분 지원
  • Discord embed 4000자 초과 시 detail 레벨 항목을 자동 축약하는 _format_log_description 추가
  • llm_router.py에서 모든 OpenAI API 호출을 자동 추적 (stage, model, tier, fallback, latency)
  • generate_service, chat_service에 작업 시작 시점 job_start 로그 추가
  • 스켈레톤 생성 시 슬롯 요약, repair/autofix 여부, 전체 완료 로그 추가
  • Google Places API 호출마다 query, geo 필터 타입, price_levels, min_rating payload 로그 추가
  • Places 검색 fallback 단계별 통계 집계 로그 추가
  • finalize 단계를 desc/time/summary 3단계로 분리
  • Chat 의도 분석 후 intent_parsed detail 로그, mutate 검색 상세 로그 추가
  • 콜백 전달 테스트에서 Discord 웹훅이 fake post를 공유하던 기존 테스트 버그 수정

3. AI 활용 및 검증

  • AI가 생성한 코드 포함

  • 100% 직접 작성

  • 검증 방법: uv run pytest 35 passed

4. 스크린샷 (선택)

5. 체크리스트

  • uv run pre-commit run --all-files를 실행하여 통과했는가?
  • 스스로 코드를 한 번 리뷰했는가? (AI가 짠 코드 맹신 금지)
  • 불필요한 주석이나 print 문을 제거했는가?

Summary by CodeRabbit

  • 새로운 기능

    • 작업 시작 시 요약 해시(query_hash) 기록 및 로그 수준(level) 필드 추가
  • 개선 사항

    • 호출별 시도 로그(성공/실패/재시도/대체)와 지연시간(ms) 반올림 기록
    • 단계별 집계 및 요약 로그(플레이스·슬롯·설명·스켈레톤 등) 추가
    • 알림 포매터: 상세 항목 접기 및 길이 제한 처리
  • 테스트

    • 테스트 환경에 HMAC_SECRET 추가 및 알림 부작용 억제 패치 적용
  • 문서/환경

    • 예제 환경파일에 HMAC_SECRET 항목 추가

zweadfx added 6 commits April 8, 2026 10:07
  - job_start 로그로 생성/수정 요청 시작 시점 추적
  - llm_router에서 모든 OpenAI API 호출 자동 추적 (stage, model, tier, latency)
  - 스켈레톤 생성 완료 시 슬롯 요약 및 repair/autofix 여부 포함
  - Google Places 검색 시 fallback 단계별 통계 집계
  - finalize 단계를 desc/time/summary 3단계로 분리
  - 의도 분석 및 mutate 검색 상세 로그 추가
  - 4000자 초과 시 detail 레벨 항목 자동 축약
  - notify_callback_failure를 no-op으로 패치하여 call_count 오염 방지
@zweadfx zweadfx self-assigned this Apr 8, 2026
@zweadfx zweadfx added the feature 사용자 또는 비즈니스에 새로운 기능을 추가하는 작업 label Apr 8, 2026
@zweadfx zweadfx requested a review from iamb0ttle as a code owner April 8, 2026 01:40
@zweadfx
Copy link
Copy Markdown
Contributor Author

zweadfx commented Apr 8, 2026

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e2912c7c-9308-46a2-84b0-c8e2bf64edf9

📥 Commits

Reviewing files that changed from the base of the PR and between c8ffa94 and 79c32e0.

📒 Files selected for processing (1)
  • .env.example
✅ Files skipped from review due to trivial changes (1)
  • .env.example

Walkthrough

작업 로깅에 level 키워드 파라미터를 도입하고, LLM 라우터·그래프 노드·서비스 전반에 상세 로그 항목을 추가했으며, 웹훅 알림 포매터는 detail 항목 축약과 길이 제한 처리를 도입했습니다.

Changes

Cohort / File(s) Summary
Job Log Context Core
app/core/job_log_context.py
append_job_log 시그니처 변경: 키워드 전용 level: str = "info" 추가; 각 로그 항목에 "level" 포함
LLM Router
app/core/llm_router.py
동기/비동기 경로에 append_job_log 통합: 시도별 성공/실패/폴백/재시도 이벤트와 라운드된 지연(ms) 값 기록으로 로깅 흐름 추가
Graph: Intent / Mutate
app/graph/chat/nodes/analyze_intent.py, app/graph/chat/nodes/mutate.py
의도 후처리 완료 시 detail 레벨 로그 추가; mutate_search_place에 결과 수·diff·검색 키워드·bbox·rerank·fallback 정보 로그 추가
Graph: Roadmap (places / skeleton / finalize)
app/graph/roadmap/nodes/places.py, app/graph/roadmap/nodes/skeleton.py, app/graph/roadmap/nodes/finalize.py
search_for_slot 반환값 확장(fallback_stage 포함), 빈 슬롯·폴백 통계 집계 및 요약 로그 추가; 스켈레톤 세그먼트별 메시지 형식 변경 및 완료 요약 로그; 최종화 단계에 장소 설명·시간 정책 로그 추가
Services: chat / generate / google_places
app/services/chat_service.py, app/services/generate_service.py, app/services/google_places_service.py
작업 시작 job_start 로그 추가(타입, job_id, 쿼리 길이/해시 등) 및 Places 검색 컨텍스트(q_len, q_hash, geo, price, min_rating) 로그 추가; HMAC_SECRET 미설정 시 런타임 예외 추가
Webhook Notification Formatter
app/services/webhook_notification.py
_MAX_DESC_LEN, _format_log_line, _format_log_description 도입: detail 레벨 항목 축약 로직과 설명 길이 트렁케이션 적용
Config / Settings
app/core/config.py
Settings에 필수 필드 HMAC_SECRET: str 추가(기본값 없음)
Tests
tests/test_callback_delivery.py, tests/test_main.py, tests/test_readiness.py, tests/test_recommend_service.py, tests/test_timeout_policy.py
테스트 환경 변수에 HMAC_SECRET 추가 설정; 일부 테스트는 실패 알림 사이드이펙트 억제를 위해 notify_callback_failure를 async no-op으로 모킹
Env Example
.env.example
.env.exampleHMAC_SECRET= 항목 추가

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client as Client
participant Service as API Service
participant JobLog as JobLogContext
participant LLM as LLM Router
participant Places as GooglePlacesService
participant Webhook as WebhookNotifier

Client->>Service: 요청 (job_id, payload)
Service->>JobLog: init_job_log(job_id)
Service->>JobLog: append_job_log("job_start", ..., level="info")
Service->>LLM: LLM 호출 (invoke/ainvoke)
LLM->>JobLog: append_job_log(... success/failure, level="info"/"detail")
Service->>Places: 장소 검색 (선택적)
Places->>JobLog: append_job_log("places_search", ..., level="detail")
Service->>JobLog: collect_job_logs()
Service->>Webhook: notify_job_completed(collected_logs)
Webhook->>Webhook: _format_log_description(collapsed detail entries)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 Discord 웹훅 로그 상세화와 로드맵 생성/수정 파이프라인 내부 추적이라는 핵심 변경 사항을 명확하게 설명하고 있으며, 구체적이고 서술적입니다.
Description check ✅ Passed 설명이 저장소의 필수 템플릿 구조를 따르고 있으며, 개요(이슈 #69), 작업 내용(10개 항목), AI 활용 여부, 검증 방법 등을 포함하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/webhook-pipeline-tracking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/graph/roadmap/nodes/places.py (1)

147-165: ⚠️ Potential issue | 🟡 Minor

region_bbox가 없는 슬롯은 지금 fb_stage가 잘못 집계됩니다.

get_region_bbox(region)None이면 첫 검색은 이미 unfiltered인데, fallback_stage 기본값이 "restriction"이라서 성공 시 fb_stats에는 restriction hit로 누적되고 places_search detail 로그도 건너뜁니다. 새 fallback 통계를 믿기 어렵게 되니 bbox 부재를 별도 stage로 분리하거나 초기값을 region 여부에 따라 설정하는 편이 맞습니다.

예시 수정안
-        fallback_stage = "restriction"
+        fallback_stage = "pending"
         restriction_used = False
         bias_used = False
         unfiltered_used = False

         region_bbox = get_region_bbox(region)
         if region_bbox is None:
             geo_missing_region_bbox = True
+            fallback_stage = "no_region_bbox"
+        else:
+            fallback_stage = "restriction"

Also applies to: 265-271, 280-286, 355-359

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/graph/roadmap/nodes/places.py` around lines 147 - 165, In
search_for_slot, when get_region_bbox(region) returns None the initial search is
already unfiltered but fallback_stage is initialized to "restriction", causing
fb_stats and places_search detail logs to misattribute the hit; change the
initialization so fallback_stage reflects whether region_bbox is present (e.g.,
set fallback_stage = "unfiltered" or a distinct "missing_region" when
region_bbox is None) and/or explicitly set fallback_stage immediately after
computing region_bbox; update the same pattern in the other similar blocks (the
later search branches that also call get_region_bbox and use
fallback_stage/fb_stats) so that fb_stats counts and places_search detail
logging use the correct stage when bbox is missing.
🧹 Nitpick comments (1)
app/core/job_log_context.py (1)

36-38: extra가 예약 필드를 덮어쓰지 못하게 막아 두는 편이 안전합니다.

이번 PR부터 level이 로그 축약 동작을 제어하는 예약 필드가 됐는데, 지금은 entry.update(extra)가 뒤에서 실행돼서 extra={"level": ...} 같은 값이 명시적 level 인자를 그대로 덮어쓸 수 있습니다. stage, message, elapsed_ms, level 정도는 예약 키로 막아 두는 편이 안전합니다.

예시 수정안
-    entry: dict[str, Any] = {"stage": stage, "message": message, "elapsed_ms": elapsed_ms, "level": level}
-    if extra:
-        entry.update(extra)
+    entry: dict[str, Any] = {"stage": stage, "message": message, "elapsed_ms": elapsed_ms, "level": level}
+    if extra:
+        reserved = {"stage", "message", "elapsed_ms", "level"}
+        entry.update({key: value for key, value in extra.items() if key not in reserved})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/core/job_log_context.py` around lines 36 - 38, The current code
unconditionally does entry.update(extra) which allows callers to overwrite
reserved fields; modify the code that builds and updates the log `entry` (the
block that creates entry: dict[str, Any] = {"stage": stage, "message": message,
"elapsed_ms": elapsed_ms, "level": level}) to filter `extra` so any keys in the
reserved set {"stage", "message", "elapsed_ms", "level"} are dropped before
merging (e.g. build a new dict from extra excluding those keys or pop those keys
from extra) and then update `entry` with the filtered dict to ensure reserved
fields cannot be overwritten.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/core/llm_router.py`:
- Around line 202-207: The llm_error paths currently log only the exception
type; update every error logging call that uses append_job_log with event
"llm_error" (and related error places noted around the append_job_log calls) to
include the same minimum common fields as the success path: include latency_ms
in the extra dict, include tier (use tier.value if tier else 'N/A') and fallback
(true/false) in the message or extra so webhook traces show which tier/fallback
path and latency caused the failure; keep the existing stage and selected_model
fields consistent with the success log format used by append_job_log("llm",
...).

In `@app/services/chat_service.py`:
- Around line 127-128: 현재 init_job_log와 append_job_log("job_start", f"type=chat
job_id={request.job_id} query={request.user_query[:60]}")에서 원문 user_query를 그대로
일부라도 남기고 있어 민감정보가 유출될 위험이 있습니다; 대신 request.user_query의 원문을 남기지 말고 길이(len), 안전한
해시(e.g., SHA256), 그리고 이미 파싱한 intent 또는 엔티티 요약만 로그에 기록하도록 변경하고(예:
append_job_log("job_start", f"type=chat job_id={request.job_id}
query_len={len(request.user_query)} query_hash={hash}
intent={intent_summary}")), 동일한 파일에서 사용된 다른 로그 패턴들(kw=, q= 계열 문자열 포함)을 찾아 같은
방식으로 교체하여 원문이 절대로 기록되지 않도록 하세요 (참조 심볼: init_job_log, append_job_log,
request.user_query, "job_start", any "kw=" / "q=" usages).

In `@app/services/google_places_service.py`:
- Around line 107-113: The detail log currently includes the raw query substring
(query[:50]) which may leak sensitive user text; update the append_job_log call
in google_places_service (around variables query, geo, price_tag, min_rating) to
omit the raw query and instead log safe metadata—e.g. include q_len (len(query))
or a short hash of query and the existing derived fields (geo, price_tag,
min_rating) so the original query text is never written to the detail log.

---

Outside diff comments:
In `@app/graph/roadmap/nodes/places.py`:
- Around line 147-165: In search_for_slot, when get_region_bbox(region) returns
None the initial search is already unfiltered but fallback_stage is initialized
to "restriction", causing fb_stats and places_search detail logs to misattribute
the hit; change the initialization so fallback_stage reflects whether
region_bbox is present (e.g., set fallback_stage = "unfiltered" or a distinct
"missing_region" when region_bbox is None) and/or explicitly set fallback_stage
immediately after computing region_bbox; update the same pattern in the other
similar blocks (the later search branches that also call get_region_bbox and use
fallback_stage/fb_stats) so that fb_stats counts and places_search detail
logging use the correct stage when bbox is missing.

---

Nitpick comments:
In `@app/core/job_log_context.py`:
- Around line 36-38: The current code unconditionally does entry.update(extra)
which allows callers to overwrite reserved fields; modify the code that builds
and updates the log `entry` (the block that creates entry: dict[str, Any] =
{"stage": stage, "message": message, "elapsed_ms": elapsed_ms, "level": level})
to filter `extra` so any keys in the reserved set {"stage", "message",
"elapsed_ms", "level"} are dropped before merging (e.g. build a new dict from
extra excluding those keys or pop those keys from extra) and then update `entry`
with the filtered dict to ensure reserved fields cannot be overwritten.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3aeabd3a-c0cd-4b8b-963f-a0a321ce759b

📥 Commits

Reviewing files that changed from the base of the PR and between 6406ae7 and a10b65e.

📒 Files selected for processing (12)
  • app/core/job_log_context.py
  • app/core/llm_router.py
  • app/graph/chat/nodes/analyze_intent.py
  • app/graph/chat/nodes/mutate.py
  • app/graph/roadmap/nodes/finalize.py
  • app/graph/roadmap/nodes/places.py
  • app/graph/roadmap/nodes/skeleton.py
  • app/services/chat_service.py
  • app/services/generate_service.py
  • app/services/google_places_service.py
  • app/services/webhook_notification.py
  • tests/test_callback_delivery.py

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/services/chat_service.py`:
- Around line 129-132: Replace the weak 8-char sha256 prefix used for query_hash
with an HMAC-based digest using a server-side secret and a longer prefix:
compute HMAC-SHA256 over request.user_query with a secret loaded from config/env
(fail loudly if missing), take a longer hex substring (e.g., 32 chars) and use
that value for query_hash in the append_job_log call; update references to
query_hash and ensure the secret access is performed securely (not checked into
source) so the change affects the calculation used in
append_job_log("job_start", ...).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fd3bdb93-7e34-40c6-b9dd-81f1bddc3f45

📥 Commits

Reviewing files that changed from the base of the PR and between a10b65e and 969d647.

📒 Files selected for processing (3)
  • app/core/llm_router.py
  • app/services/chat_service.py
  • app/services/google_places_service.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/services/google_places_service.py
  • app/core/llm_router.py

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/core/config.py`:
- Line 18: You made HMAC_SECRET a required setting (HMAC_SECRET in Settings), so
update the sample env file (.env.example) to include HMAC_SECRET with a clear
placeholder value and a short comment; add a line like
HMAC_SECRET=your_hmac_secret_here (or similar) so new deployments won’t fail
Settings initialization, and ensure any README or docs referencing required env
vars mention HMAC_SECRET as mandatory.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26d1ace2-0039-4051-9c9c-cd6d3a90bd82

📥 Commits

Reviewing files that changed from the base of the PR and between 969d647 and c8ffa94.

📒 Files selected for processing (8)
  • app/core/config.py
  • app/services/chat_service.py
  • app/services/google_places_service.py
  • tests/test_callback_delivery.py
  • tests/test_main.py
  • tests/test_readiness.py
  • tests/test_recommend_service.py
  • tests/test_timeout_policy.py
✅ Files skipped from review due to trivial changes (2)
  • tests/test_readiness.py
  • tests/test_recommend_service.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/test_callback_delivery.py
  • app/services/chat_service.py
  • app/services/google_places_service.py

@zweadfx
Copy link
Copy Markdown
Contributor Author

zweadfx commented Apr 8, 2026

@CodeRabbit ignore

1 similar comment
@zweadfx
Copy link
Copy Markdown
Contributor Author

zweadfx commented Apr 8, 2026

@CodeRabbit ignore

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

@zweadfx: Sure! I'll skip the review for this PR and pause automatic reviews.

✅ Actions performed

Reviews paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

✅ Actions performed

Reviews paused.

1 similar comment
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

✅ Actions performed

Reviews paused.

Copy link
Copy Markdown
Member

@iamb0ttle iamb0ttle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@zweadfx zweadfx merged commit a69502b into main Apr 8, 2026
3 checks passed
@zweadfx zweadfx deleted the feat/webhook-pipeline-tracking branch April 8, 2026 03:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature 사용자 또는 비즈니스에 새로운 기능을 추가하는 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] Discord 웹훅 로그 상세화 — 로드맵 생성/수정 파이프라인 내부 추적

2 participants