@dssp/dcsp 1.0.2 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist-client/bootstrap.js +13 -10
  2. package/dist-client/bootstrap.js.map +1 -1
  3. package/dist-client/components/kpi-2d-lookup-chart.d.ts +50 -0
  4. package/dist-client/components/kpi-2d-lookup-chart.js +263 -0
  5. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  6. package/dist-client/components/kpi-boxplot-chart.d.ts +31 -0
  7. package/dist-client/components/kpi-boxplot-chart.js +306 -0
  8. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  9. package/dist-client/components/kpi-lookup-chart.d.ts +58 -0
  10. package/dist-client/components/kpi-lookup-chart.js +464 -0
  11. package/dist-client/components/kpi-lookup-chart.js.map +1 -0
  12. package/dist-client/components/kpi-mini-trend-chart.d.ts +14 -0
  13. package/dist-client/components/kpi-mini-trend-chart.js +180 -0
  14. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -0
  15. package/dist-client/components/kpi-radar-chart.d.ts +17 -0
  16. package/dist-client/components/kpi-radar-chart.js +259 -0
  17. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  18. package/dist-client/components/kpi-single-boxplot-chart.d.ts +36 -0
  19. package/dist-client/components/kpi-single-boxplot-chart.js +417 -0
  20. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  21. package/dist-client/components/kpi-step-lookup-chart.d.ts +33 -0
  22. package/dist-client/components/kpi-step-lookup-chart.js +181 -0
  23. package/dist-client/components/kpi-step-lookup-chart.js.map +1 -0
  24. package/dist-client/components/kpi-trend-chart.d.ts +25 -0
  25. package/dist-client/components/kpi-trend-chart.js +241 -0
  26. package/dist-client/components/kpi-trend-chart.js.map +1 -0
  27. package/dist-client/google-map/common-google-map.d.ts +35 -0
  28. package/dist-client/google-map/common-google-map.js +349 -0
  29. package/dist-client/google-map/common-google-map.js.map +1 -0
  30. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  31. package/dist-client/google-map/google-map-loader.js +23 -0
  32. package/dist-client/google-map/google-map-loader.js.map +1 -0
  33. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +47 -0
  34. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +393 -0
  35. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -0
  36. package/dist-client/pages/kpi-admin/kpi-system-guide.d.ts +18 -0
  37. package/dist-client/pages/kpi-admin/kpi-system-guide.js +696 -0
  38. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -0
  39. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +46 -0
  40. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +945 -0
  41. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  42. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +34 -0
  43. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +238 -0
  44. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  45. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +37 -0
  46. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +689 -0
  47. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  48. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +57 -0
  49. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +866 -0
  50. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  51. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +69 -0
  52. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +790 -0
  53. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  54. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +22 -0
  55. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +344 -0
  56. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  57. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +21 -0
  58. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +335 -0
  59. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  60. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +51 -0
  61. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +693 -0
  62. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -0
  63. package/dist-client/pages/sv-project-complete.d.ts +31 -0
  64. package/dist-client/pages/sv-project-complete.js +364 -0
  65. package/dist-client/pages/sv-project-complete.js.map +1 -0
  66. package/dist-client/pages/sv-project-detail.d.ts +73 -0
  67. package/dist-client/pages/sv-project-detail.js +1387 -0
  68. package/dist-client/pages/sv-project-detail.js.map +1 -0
  69. package/dist-client/route.d.ts +1 -1
  70. package/dist-client/route.js +15 -0
  71. package/dist-client/route.js.map +1 -1
  72. package/dist-client/shared/complete-api.d.ts +45 -0
  73. package/dist-client/shared/complete-api.js +274 -0
  74. package/dist-client/shared/complete-api.js.map +1 -0
  75. package/dist-client/shared/func.d.ts +2 -0
  76. package/dist-client/shared/func.js +22 -0
  77. package/dist-client/shared/func.js.map +1 -0
  78. package/dist-client/shared/integration-fetch.d.ts +35 -0
  79. package/dist-client/shared/integration-fetch.js +53 -0
  80. package/dist-client/shared/integration-fetch.js.map +1 -0
  81. package/dist-client/shared/kpi-project-sync.d.ts +16 -0
  82. package/dist-client/shared/kpi-project-sync.js +129 -0
  83. package/dist-client/shared/kpi-project-sync.js.map +1 -0
  84. package/dist-client/tsconfig.tsbuildinfo +1 -1
  85. package/dist-client/viewparts/menu-tools.d.ts +7 -0
  86. package/dist-client/viewparts/menu-tools.js +168 -21
  87. package/dist-client/viewparts/menu-tools.js.map +1 -1
  88. package/dist-server/index.d.ts +2 -0
  89. package/dist-server/index.js +5 -0
  90. package/dist-server/index.js.map +1 -1
  91. package/dist-server/middlewares/index.d.ts +7 -0
  92. package/dist-server/middlewares/index.js +100 -0
  93. package/dist-server/middlewares/index.js.map +1 -0
  94. package/dist-server/service/index.d.ts +4 -0
  95. package/dist-server/service/index.js +19 -0
  96. package/dist-server/service/index.js.map +1 -0
  97. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  98. package/dist-server/service/kpi-metric-value/index.js +8 -0
  99. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  100. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +198 -0
  101. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +1067 -0
  102. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  103. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +15 -0
  104. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +72 -0
  105. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  106. package/dist-server/service/kpi-stat/index.d.ts +4 -0
  107. package/dist-server/service/kpi-stat/index.js +8 -0
  108. package/dist-server/service/kpi-stat/index.js.map +1 -0
  109. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +13 -0
  110. package/dist-server/service/kpi-stat/kpi-stat-query.js +751 -0
  111. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -0
  112. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +32 -0
  113. package/dist-server/service/kpi-stat/kpi-stat-types.js +126 -0
  114. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -0
  115. package/dist-server/service/kpi-value/index.d.ts +3 -0
  116. package/dist-server/service/kpi-value/index.js +7 -0
  117. package/dist-server/service/kpi-value/index.js.map +1 -0
  118. package/dist-server/service/kpi-value/kpi-value-query.d.ts +8 -0
  119. package/dist-server/service/kpi-value/kpi-value-query.js +90 -0
  120. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  121. package/dist-server/tsconfig.tsbuildinfo +1 -1
  122. package/package.json +8 -7
  123. package/python/RUNNER_CONVERSION_GUIDE.md +81 -0
  124. package/python/doc_summary_engine_handoff/.env.example +21 -0
  125. package/python/doc_summary_engine_handoff/HANDOFF.md +266 -0
  126. package/python/doc_summary_engine_handoff/README.md +71 -0
  127. package/python/doc_summary_engine_handoff/__main__.py +159 -0
  128. package/python/doc_summary_engine_handoff/api.py +165 -0
  129. package/python/doc_summary_engine_handoff/extract.py +290 -0
  130. package/python/doc_summary_engine_handoff/requirements.txt +15 -0
  131. package/python/doc_summary_engine_handoff/summarize.py +247 -0
  132. package/python/inspection_ai_engine_handoff/.env.example +22 -0
  133. package/python/inspection_ai_engine_handoff/HANDOFF.md +541 -0
  134. package/python/inspection_ai_engine_handoff/README.md +83 -0
  135. package/python/inspection_ai_engine_handoff/__main__.py +169 -0
  136. package/python/inspection_ai_engine_handoff/activities.py +147 -0
  137. package/python/inspection_ai_engine_handoff/api.py +273 -0
  138. package/python/inspection_ai_engine_handoff/checklist.py +53 -0
  139. package/python/inspection_ai_engine_handoff/cv_handlers.py +46 -0
  140. package/python/inspection_ai_engine_handoff/cv_model_registry.py +252 -0
  141. package/python/inspection_ai_engine_handoff/cv_models/README.md +58 -0
  142. package/python/inspection_ai_engine_handoff/cv_models/_example_frame_alignment/handler.py +39 -0
  143. package/python/inspection_ai_engine_handoff/cv_models/_example_frame_alignment/model.json +22 -0
  144. package/python/inspection_ai_engine_handoff/engine.py +333 -0
  145. package/python/inspection_ai_engine_handoff/requirements.txt +12 -0
  146. package/python/inspection_ai_engine_handoff/router.py +200 -0
  147. package/python/inspection_ai_engine_handoff/shot_catalog.py +79 -0
  148. package/python/inspection_ai_engine_handoff/shot_planner.py +278 -0
  149. package/python/inspection_ai_engine_handoff/vlm.py +335 -0
  150. package/python/spec-matching-0.5.5/README.md +113 -0
  151. package/python/spec-matching-0.5.5/__main__.py +331 -0
  152. package/python/spec-matching-0.5.5/kcs.csv +6028 -0
  153. package/python/spec-matching-0.5.5/kcs_embeddings.meta.json +1 -0
  154. package/python/spec-matching-0.5.5/kcs_embeddings.npy +0 -0
  155. package/python/spec-matching-0.5.5/requirements.txt +3 -0
  156. package/python/spec-matching-0.5.5//342/230/2057 SF-TD4 /341/204/221/341/205/263/341/204/205/341/205/251/341/204/200/341/205/263/341/204/205/341/205/242/341/206/267/341/204/211/341/205/245/341/206/257/341/204/200/341/205/250/341/204/211/341/205/245 /341/204/213/341/205/243/341/206/274/341/204/211/341/205/265/341/206/250_V1.0.hwp +0 -0
  157. package/schema.graphql +1274 -22
  158. package/things-factory.config.js +7 -1
  159. package/translations/en.json +2 -0
  160. package/translations/ja.json +2 -0
  161. package/translations/ko.json +2 -0
@@ -0,0 +1,247 @@
1
+ """추출된 본문 → 한 줄 AI 브리핑 (OpenAI).
2
+
3
+ 설계 — 환각 최소화:
4
+ - 본문에 실제로 있는 내용만 사용하도록 강하게 지시.
5
+ - 긴 문서는 map-reduce: 파트별 '사실 추출' → 통합 → 최종 브리핑
6
+ (한 번에 욱여넣어 잘리는 것보다 사실 보존에 유리).
7
+ - 본문이 빈약/추출 실패면 애초에 이 모듈을 호출하지 않음 (api.py 가 거름).
8
+ - 모델이 판단 불가라고 보면 briefing=null 로 돌려주도록 허용.
9
+
10
+ 프롬프트 문구는 채팅에서 합의된 초안을 그대로 사용.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import os
16
+ import re
17
+ import time
18
+ from dataclasses import dataclass
19
+ from typing import Any, Dict, Optional
20
+
21
+
22
+ _DEFAULT_MODEL = os.environ.get("OPENAI_MODEL", "gpt-5.5")
23
+ # 브리핑은 충실한 압축이 목적 → reasoning 은 낮게 (속도·비용↓, 환각 여지↓).
24
+ _REASONING_EFFORT = os.environ.get("DOC_BRIEF_REASONING_EFFORT", "low").strip()
25
+
26
+ # 한 번에 처리할 본문 길이(글자). 넘으면 map-reduce.
27
+ _SINGLE_PASS_CHARS = int(os.environ.get("DOC_SINGLE_PASS_CHARS", "12000"))
28
+ # map 단계 청크 크기(글자).
29
+ _CHUNK_CHARS = int(os.environ.get("DOC_CHUNK_CHARS", "8000"))
30
+
31
+
32
+ @dataclass
33
+ class BriefResult:
34
+ briefing: Optional[str] # 한 줄 브리핑. 판단 불가 시 None.
35
+ doc_type: str # 공문|보고서|회의록|계획서|기타 (본문 근거 시에만)
36
+ has_content: bool # 모델이 요지를 잡았는지
37
+ model: str
38
+ elapsed_sec: float
39
+ note: str = "" # briefing=None 일 때 사유
40
+ raw: str = "" # 디버그용 원본 응답
41
+
42
+
43
+ # ─── 합의된 브리핑 프롬프트 ────────────────────────────────────────────────
44
+
45
+ _BRIEF_PROMPT = """당신은 건설 시공감리 문서를 정리하는 비서입니다.
46
+ 아래 [문서 본문]을 읽고, 감리자·시공자가 클릭 한 번에 내용을 파악할 수 있는
47
+ 한 줄 브리핑을 작성하세요.
48
+
49
+ [작성 규칙]
50
+ 1. 본문에 실제로 적힌 내용만 사용. 본문에 없는 사실·배경·추측은 절대 쓰지 마라.
51
+ 2. 1~2문장, 한국어 명사형 종결("~건입니다", "~알림입니다", "~보고서입니다").
52
+ 3. 가능하면 다음 요소를 본문에 있는 한도에서 담아라:
53
+ - 누가(발신/수신 기관·부서), 무엇을(핵심 안건·조치), 언제(날짜), 문서 종류.
54
+ 4. 날짜·금액·수치·고유명사는 본문에 적힌 그대로만. 본문에 없으면 쓰지 마라.
55
+ 5. 본문이 비었거나 너무 짧아 요지를 알 수 없으면, 지어내지 말고
56
+ briefing 을 null 로 반환하라.
57
+ 6. 문서 분류(category)는 참고만. 분류 이름을 근거로 내용을 추측하지 마라.
58
+
59
+ [문서 분류] {category}
60
+ [파일명] {filename}
61
+ [문서 본문]
62
+ {text}
63
+
64
+ 아래 JSON 하나만 출력:
65
+ {{
66
+ "briefing": "<한 줄 브리핑 또는 null>",
67
+ "doc_type": "<공문|보고서|회의록|계획서|기타 — 본문 근거 시에만>",
68
+ "has_content": true
69
+ }}"""
70
+
71
+ # map 단계: 긴 문서의 한 파트에서 '실제로 적힌 사실'만 뽑기 (브리핑은 reduce 에서).
72
+ _FACTS_PROMPT = """당신은 건설 시공감리 문서를 정리하는 비서입니다.
73
+ 아래는 긴 문서의 일부(파트 {k}/{n})입니다.
74
+ 이 파트에 '실제로 적힌' 핵심 사실만 짧은 항목(-)으로 뽑으세요.
75
+ 본문에 없는 내용·추측은 절대 쓰지 마라. 날짜·숫자·고유명사는 적힌 그대로.
76
+ 이 파트에 의미 있는 내용이 없으면 빈 줄만 출력.
77
+
78
+ [문서 본문 — 파트 {k}/{n}]
79
+ {chunk}
80
+
81
+ 핵심 사실:"""
82
+
83
+
84
+ # ─── 유틸 ──────────────────────────────────────────────────────────────────
85
+
86
+ def _extract_json(text: str) -> Optional[dict]:
87
+ """LLM 출력에서 첫 JSON 객체 추출. 실패 시 None."""
88
+ if not text:
89
+ return None
90
+ m = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL)
91
+ candidate = m.group(1) if m else None
92
+ if candidate is None:
93
+ s, e = text.find("{"), text.rfind("}")
94
+ if s != -1 and e != -1 and e > s:
95
+ candidate = text[s : e + 1]
96
+ if not candidate:
97
+ return None
98
+ try:
99
+ return json.loads(candidate)
100
+ except json.JSONDecodeError:
101
+ return None
102
+
103
+
104
+ def _normalize_content(raw: Any) -> str:
105
+ """OpenAI 응답 content 가 str/list 어떤 형식이든 평탄화."""
106
+ if raw is None:
107
+ return ""
108
+ if isinstance(raw, str):
109
+ return raw
110
+ if isinstance(raw, list):
111
+ parts = []
112
+ for item in raw:
113
+ t = item.get("text") if isinstance(item, dict) else getattr(item, "text", None)
114
+ if isinstance(t, str):
115
+ parts.append(t)
116
+ return "".join(parts)
117
+ return str(raw)
118
+
119
+
120
+ def _call_text(client, prompt: str, *, model: str, max_completion_tokens: int,
121
+ reasoning_effort: Optional[str], retries: int = 1) -> str:
122
+ """텍스트 프롬프트 → 모델 응답 문자열. reasoning 미지원 모델은 자동 fallback."""
123
+ effort = reasoning_effort if reasoning_effort is not None else _REASONING_EFFORT
124
+ last = ""
125
+ for _ in range(retries + 1):
126
+ kwargs: Dict[str, Any] = {
127
+ "model": model,
128
+ "messages": [{"role": "user", "content": prompt}],
129
+ "max_completion_tokens": max_completion_tokens,
130
+ }
131
+ if effort and effort != "none":
132
+ kwargs["reasoning_effort"] = effort
133
+ try:
134
+ resp = client.chat.completions.create(**kwargs)
135
+ return _normalize_content(resp.choices[0].message.content)
136
+ except Exception as e:
137
+ msg = str(e)
138
+ last = msg
139
+ # reasoning_effort 미지원 모델이면 빼고 재시도 (temperature 는 안 건드림).
140
+ if "reasoning" in msg.lower() or "unsupported" in msg.lower():
141
+ kwargs.pop("reasoning_effort", None)
142
+ try:
143
+ resp = client.chat.completions.create(**kwargs)
144
+ return _normalize_content(resp.choices[0].message.content)
145
+ except Exception as e2:
146
+ last = f"{msg} | retry: {e2}"
147
+ raise RuntimeError(last or "LLM 호출 실패")
148
+
149
+
150
+ def _chunks(text: str, size: int) -> list[str]:
151
+ return [text[i : i + size] for i in range(0, len(text), size)] or [""]
152
+
153
+
154
+ def _reduce_to_facts(client, text: str, *, model: str) -> str:
155
+ """긴 본문 → 파트별 사실 추출 후 합침 (map 단계)."""
156
+ parts = _chunks(text, _CHUNK_CHARS)
157
+ n = len(parts)
158
+ facts: list[str] = []
159
+ for k, chunk in enumerate(parts, 1):
160
+ prompt = _FACTS_PROMPT.format(k=k, n=n, chunk=chunk)
161
+ try:
162
+ out = _call_text(
163
+ client, prompt, model=model,
164
+ max_completion_tokens=800, reasoning_effort="low",
165
+ )
166
+ if out.strip():
167
+ facts.append(out.strip())
168
+ except Exception:
169
+ continue # 한 파트 실패해도 나머지로 진행
170
+ return "\n".join(facts)
171
+
172
+
173
+ # ─── 진입점 ────────────────────────────────────────────────────────────────
174
+
175
+ def brief_document(
176
+ text: str,
177
+ *,
178
+ filename: str = "",
179
+ category: str = "",
180
+ client=None,
181
+ model: Optional[str] = None,
182
+ reasoning_effort: Optional[str] = None,
183
+ ) -> BriefResult:
184
+ """추출된 본문 → 한 줄 브리핑.
185
+
186
+ Args:
187
+ text: extract.extract_text() 가 뽑은 본문 (충분한 분량이라고 가정).
188
+ filename: 파일명 (근거 힌트).
189
+ category: 문서 분류 (대/중/소분류 문자열, 근거 힌트).
190
+ client: 기존 OpenAI 클라이언트 재사용. None 이면 새로 생성.
191
+ model: 모델 이름. None 이면 OPENAI_MODEL 환경변수.
192
+ """
193
+ t0 = time.time()
194
+ model = model or _DEFAULT_MODEL
195
+ if client is None:
196
+ from openai import OpenAI
197
+ client = OpenAI()
198
+
199
+ body = text or ""
200
+ # 긴 문서는 먼저 사실만 뽑아서 압축 (map-reduce).
201
+ if len(body) > _SINGLE_PASS_CHARS:
202
+ facts = _reduce_to_facts(client, body, model=model)
203
+ body = facts if facts.strip() else body[:_SINGLE_PASS_CHARS]
204
+
205
+ prompt = _BRIEF_PROMPT.format(
206
+ category=(category or "(미상)"),
207
+ filename=(filename or "(미상)"),
208
+ text=body,
209
+ )
210
+ try:
211
+ raw = _call_text(
212
+ client, prompt, model=model,
213
+ max_completion_tokens=1000, reasoning_effort=reasoning_effort,
214
+ )
215
+ except Exception as e:
216
+ return BriefResult(
217
+ briefing=None, doc_type="기타", has_content=False,
218
+ model=model, elapsed_sec=round(time.time() - t0, 2),
219
+ note=f"LLM 호출 실패: {str(e)[:160]}", raw="",
220
+ )
221
+
222
+ data = _extract_json(raw)
223
+ if not data:
224
+ return BriefResult(
225
+ briefing=None, doc_type="기타", has_content=False,
226
+ model=model, elapsed_sec=round(time.time() - t0, 2),
227
+ note=f"JSON 파싱 실패: {raw[:160]}", raw=raw,
228
+ )
229
+
230
+ briefing = data.get("briefing")
231
+ if isinstance(briefing, str):
232
+ briefing = briefing.strip() or None
233
+ else:
234
+ briefing = None # null/누락/비문자열 → None
235
+
236
+ doc_type = str(data.get("doc_type") or "기타").strip() or "기타"
237
+ has_content = bool(data.get("has_content")) and briefing is not None
238
+
239
+ return BriefResult(
240
+ briefing=briefing,
241
+ doc_type=doc_type,
242
+ has_content=has_content,
243
+ model=model,
244
+ elapsed_sec=round(time.time() - t0, 2),
245
+ note="" if briefing else "모델이 본문에서 요지를 확정하지 못함",
246
+ raw=raw,
247
+ )
@@ -0,0 +1,22 @@
1
+ # 필수: OpenAI API 키
2
+ OPENAI_API_KEY=sk-...
3
+
4
+ # 사용할 모델 (VLM/라우터/샷플래너 공용)
5
+ OPENAI_MODEL=gpt-5.5
6
+
7
+ # reasoning 강도 (none|low|medium|high|xhigh)
8
+ # - reasoning 모델일 때만 적용. 미지원 모델이면 자동으로 무시 + temperature=0 fallback.
9
+ REASONING_EFFORT=high
10
+
11
+ # ── HTTP API (api.py) ─────────────────────────────────────────────
12
+ INSPECTION_API_PORT=8000
13
+ INSPECTION_API_MAX_IMAGES=8
14
+ INSPECTION_API_MAX_BYTES=15728640
15
+ INSPECTION_API_ALLOWED_ORIGINS=*
16
+
17
+ # ── CV 모델 레지스트리 ────────────────────────────────────────────
18
+ # 경로 변경하고 싶을 때만 설정 (기본은 같은 폴더의 cv_models/ 디렉터리)
19
+ # CV_MODELS_DIR=./cv_models
20
+
21
+ # ── Gradio 데모 ──────────────────────────────────────────────────
22
+ DEMO_UI_PORT=7870