Corin1998 commited on
Commit
3edc30f
·
verified ·
1 Parent(s): 92a0478

Update core/ai_client.py

Browse files
Files changed (1) hide show
  1. core/ai_client.py +8 -121
core/ai_client.py CHANGED
@@ -1,125 +1,12 @@
1
- import os, base64, json
2
- from typing import List, Dict, Any, Optional
3
 
4
- try:
5
- from openai import OpenAI
6
- except Exception:
7
- OpenAI = None # 起動は継続、UIだけでも出す
8
 
9
- OPENAI_MODEL_VISION = os.environ.get("OPENAI_VISION_MODEL", "gpt-4o-mini")
10
- OPENAI_MODEL_TEXT = os.environ.get("OPENAI_TEXT_MODEL", "gpt-4o-mini")
11
-
12
- SYSTEM_JSON = """あなたは有能な財務アナリストです。
13
- 与えられた決算書(画像またはテキスト)から、次の厳密な JSON 構造のみを日本語の単位なし・半角数値で返してください。分からない項目は null。
14
- {
15
- "company": {"name": null},
16
- "period": {"start_date": null, "end_date": null},
17
- "balance_sheet": {
18
- "total_assets": null, "total_liabilities": null, "total_equity": null,
19
- "current_assets": null, "fixed_assets": null,
20
- "current_liabilities": null, "long_term_liabilities": null
21
- },
22
- "income_statement": {
23
- "sales": null, "cost_of_sales": null, "gross_profit": null,
24
- "operating_expenses": null, "operating_income": null,
25
- "ordinary_income": null, "net_income": null
26
- },
27
- "cash_flows": {
28
- "operating_cash_flow": null, "investing_cash_flow": null, "financing_cash_flow": null
29
- }
30
- }
31
- """
32
-
33
- def _b64(img: bytes) -> str:
34
- import base64 as _b
35
- return _b.b64encode(img).decode("utf-8")
36
-
37
- def _client() -> Optional[OpenAI]:
38
- if OpenAI is None:
39
- return None
40
  key = os.environ.get("OPENAI_API_KEY")
41
  if not key:
42
- return None
43
- return OpenAI(api_key=key, timeout=30) # proxies は渡さない
44
-
45
- def extract_financials(images: Optional[List[bytes]], text_blob: Optional[str], company_hint: str) -> Dict[str, Any]:
46
- """
47
- OpenAI が使えない場合は「空の既定JSON」を返す(UIは必ず表示される)
48
- """
49
- base: Dict[str, Any] = {
50
- "company": {"name": company_hint or None},
51
- "period": {"start_date": None, "end_date": None},
52
- "balance_sheet": {
53
- "total_assets": None, "total_liabilities": None, "total_equity": None,
54
- "current_assets": None, "fixed_assets": None,
55
- "current_liabilities": None, "long_term_liabilities": None
56
- },
57
- "income_statement": {
58
- "sales": None, "cost_of_sales": None, "gross_profit": None,
59
- "operating_expenses": None, "operating_income": None,
60
- "ordinary_income": None, "net_income": None
61
- },
62
- "cash_flows": {
63
- "operating_cash_flow": None, "investing_cash_flow": None, "financing_cash_flow": None
64
- }
65
- }
66
-
67
- client = _client()
68
- if client is None:
69
- return base # キー未設定 or ライブラリ不在でも落とさない
70
-
71
- if images:
72
- content = [{"type": "text", "text": SYSTEM_JSON}]
73
- if company_hint:
74
- content.append({"type": "text", "text": f"会社名の候補: {company_hint}"})
75
- for im in images:
76
- content.append({"type": "input_image", "image_url": f"data:image/png;base64,{_b64(im)}"})
77
- resp = client.chat.completions.create(
78
- model=OPENAI_MODEL_VISION,
79
- messages=[
80
- {"role": "system", "content": "返答は必ず有効な JSON オブジェクトのみ。説明を含めない。"},
81
- {"role": "user", "content": content},
82
- ],
83
- response_format={"type": "json_object"},
84
- temperature=0.1,
85
- )
86
- return json.loads(resp.choices[0].message.content or "{}") or base
87
-
88
- else:
89
- prompt = f"{SYSTEM_JSON}\n\n以下は決算書のテキストです。上記の JSON だけを返してください。\n\n{text_blob or ''}"
90
- resp = client.chat.completions.create(
91
- model=OPENAI_MODEL_TEXT,
92
- messages=[
93
- {"role": "system", "content": "返答は必ず有効な JSON オブジェクトのみ。"},
94
- {"role": "user", "content": prompt},
95
- ],
96
- response_format={"type": "json_object"},
97
- temperature=0.1,
98
- )
99
- return json.loads(resp.choices[0].message.content or "{}") or base
100
-
101
- def short_insight(fin: Dict[str, Any], score: Dict[str, Any]) -> str:
102
- client = _client()
103
- if client is None:
104
- return "(OpenAI キー未設定のため AI 所見は省略)"
105
- prompt = f"""次の財務データとスコア結果から、箇条書きで短く日本語でコメントしてください。
106
- - 良い点 3つ
107
- - 懸念点 3つ
108
- - 総評(100字以内)
109
-
110
- [財務データ]
111
- {json.dumps(fin, ensure_ascii=False)}
112
-
113
- [スコア]
114
- {json.dumps(score, ensure_ascii=False)}
115
- """
116
- try:
117
- resp = client.chat.completions.create(
118
- model=OPENAI_MODEL_TEXT,
119
- messages=[{"role": "system", "content": "簡潔で公正な財務アナリスト。"},
120
- {"role": "user", "content": prompt}],
121
- temperature=0.3,
122
- )
123
- return resp.choices[0].message.content or "(AI 所見なし)"
124
- except Exception as e:
125
- return f"AI所見の生成に失敗: {e}"
 
1
+ import os
2
+ from openai import OpenAI
3
 
4
+ VISION_MODEL = os.environ.get("OPENAI_VISION_MODEL", "gpt-4o-mini")
5
+ TEXT_MODEL = os.environ.get("OPENAI_TEXT_MODEL", "gpt-4o-mini")
 
 
6
 
7
+ def get_client() -> OpenAI:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  key = os.environ.get("OPENAI_API_KEY")
9
  if not key:
10
+ raise RuntimeError("OPENAI_API_KEY が未設定です。環境変数に設定してください。")
11
+ # proxies は渡さない(OpenAI 1.x では不正引数になり得る)
12
+ return OpenAI(api_key=key, timeout=30)