Corin1998 commited on
Commit
8344d24
·
verified ·
1 Parent(s): 44dd3d1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +20 -22
app.py CHANGED
@@ -14,42 +14,40 @@ from pipelines.skills import extract_skills
14
  from pipelines.scoring import compute_quality_score
15
  from pipelines.utils import detect_filetype, load_doc_text
16
 
17
- APP_TITLE = "候補者インテーク & レジュメ標準化(安定版・最小機能)"
18
 
19
 
20
  def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
21
  """
22
- 安定版(最小機能):
23
- - OCR/抽出 → 構造化 → 正規化 → マージ → スキル抽出 → 要約 → 品質スコア
24
- - 匿名化PDF生成・HF Datasets保存は一旦オフ
25
- 入力:
26
- filepaths: gr.Files(type="filepath") からのパス配列
27
- 出力:
28
- JSON文字列 / スキルJSON文字列 / スコアJSON文字列 / 要約(3種)
29
  """
30
  if not filepaths:
31
  raise gr.Error("少なくとも1ファイルをアップロードしてください。")
32
 
33
  partial_records = []
34
- raw_texts = []
35
 
36
  for path in filepaths:
37
- # パスからバイトとファイル名を取得
38
  with open(path, "rb") as rf:
39
  raw_bytes = rf.read()
40
- fname = os.path.basename(path)
41
  filetype = detect_filetype(fname, raw_bytes)
42
 
43
- # 1) テキスト抽出:画像/PDFはOpenAI Vision OCR、docx/txtは生文面+OpenAI整形
44
  if filetype in {"pdf", "image"}:
45
  text = extract_text_with_openai(raw_bytes, filename=fname, filetype=filetype)
46
  else:
47
  base_text = load_doc_text(filetype, raw_bytes)
48
  text = extract_text_with_openai(base_text.encode("utf-8"), filename=fname, filetype="txt")
49
 
50
- raw_texts.append({"filename": fname, "text": text})
51
 
52
- # 2) OpenAIでセクション構造化 → 3) 正規化
53
  structured = structure_with_openai(text)
54
  normalized = normalize_resume({
55
  "work_experience": structured.get("work_experience_raw", ""),
@@ -65,11 +63,11 @@ def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
65
  "normalized": normalized,
66
  })
67
 
68
- # 4) 複数ファイルの統合
69
  merged = merge_normalized_records([r["normalized"] for r in partial_records])
70
 
71
- # 5) スキル抽出(辞書/正規表現)
72
- merged_text = "\n\n".join([r["text"] for r in partial_records])
73
  skills = extract_skills(merged_text, {
74
  "work_experience": merged.get("raw_sections", {}).get("work_experience", ""),
75
  "education": merged.get("raw_sections", {}).get("education", ""),
@@ -83,7 +81,7 @@ def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
83
  # 7) 要約(300/100/1文)
84
  summaries = summarize_with_openai(merged_text)
85
 
86
- # 8) 構造化出力(文字列にして Code に安全に渡す)
87
  result_json = {
88
  "candidate_id": candidate_id or hashlib.sha256(merged_text.encode("utf-8")).hexdigest()[:16],
89
  "files": [os.path.basename(p) for p in filepaths],
@@ -105,10 +103,10 @@ def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
105
 
106
 
107
  with gr.Blocks(title=APP_TITLE) as demo:
108
- gr.Markdown(f"# {APP_TITLE}\nOpenAIでOCR/構造化/要約→統合→スコア(匿名化・HF保存なしの安定版)")
109
 
110
  with gr.Row():
111
- # ★ Gradio v4仕様: Filesは type='filepath' or 'binary' のみ
112
  in_files = gr.Files(
113
  label="レジュメ類 (PDF/画像/Word/テキスト) 複数可",
114
  file_count="multiple",
@@ -124,7 +122,7 @@ with gr.Blocks(title=APP_TITLE) as demo:
124
  out_json = gr.Code(label="統合出力 (JSON)")
125
 
126
  with gr.Tab("抽出スキル"):
127
- # ★ JSONスキーマ推論の例外回避のため Code を使用
128
  out_skills = gr.Code(label="スキル一覧 (JSON)")
129
 
130
  with gr.Tab("品質スコア"):
@@ -143,5 +141,5 @@ with gr.Blocks(title=APP_TITLE) as demo:
143
 
144
 
145
  if __name__ == "__main__":
146
- # localhost 到達不可環境でも動くように share=True を明示
147
  demo.launch(share=True, server_name="0.0.0.0", server_port=7860)
 
14
  from pipelines.scoring import compute_quality_score
15
  from pipelines.utils import detect_filetype, load_doc_text
16
 
17
+ APP_TITLE = "候補者インテーク & レジュメ標準化(安定・軽量版)"
18
 
19
 
20
  def process_resumes(filepaths, candidate_id: str, additional_notes: str = ""):
21
  """
22
+ 軽量安定版:
23
+ - OCR/抽出 → 構造化 → 正規化 → 統合 → スキル抽出 → 要約 → スコア
24
+ - 匿名化PDFHF保存など重い処理は全てOFF
25
+ 入力: gr.Files(type="filepath") のパス配列
26
+ 出力: JSON文字列 / スキルJSON文字列 / スコアJSON文字列 / 要約(3種)
 
 
27
  """
28
  if not filepaths:
29
  raise gr.Error("少なくとも1ファイルをアップロードしてください。")
30
 
31
  partial_records = []
32
+ merged_plain_texts = []
33
 
34
  for path in filepaths:
35
+ fname = os.path.basename(path)
36
  with open(path, "rb") as rf:
37
  raw_bytes = rf.read()
38
+
39
  filetype = detect_filetype(fname, raw_bytes)
40
 
41
+ # 1) テキスト抽出
42
  if filetype in {"pdf", "image"}:
43
  text = extract_text_with_openai(raw_bytes, filename=fname, filetype=filetype)
44
  else:
45
  base_text = load_doc_text(filetype, raw_bytes)
46
  text = extract_text_with_openai(base_text.encode("utf-8"), filename=fname, filetype="txt")
47
 
48
+ merged_plain_texts.append(text)
49
 
50
+ # 2) 構造化 → 3) 正規化
51
  structured = structure_with_openai(text)
52
  normalized = normalize_resume({
53
  "work_experience": structured.get("work_experience_raw", ""),
 
63
  "normalized": normalized,
64
  })
65
 
66
+ # 4) 統合(複数ファイル→1候補者)
67
  merged = merge_normalized_records([r["normalized"] for r in partial_records])
68
 
69
+ # 5) スキル抽出(軽量辞書/正規表現)
70
+ merged_text = "\n\n".join(merged_plain_texts)
71
  skills = extract_skills(merged_text, {
72
  "work_experience": merged.get("raw_sections", {}).get("work_experience", ""),
73
  "education": merged.get("raw_sections", {}).get("education", ""),
 
81
  # 7) 要約(300/100/1文)
82
  summaries = summarize_with_openai(merged_text)
83
 
84
+ # 8) 構造化出力
85
  result_json = {
86
  "candidate_id": candidate_id or hashlib.sha256(merged_text.encode("utf-8")).hexdigest()[:16],
87
  "files": [os.path.basename(p) for p in filepaths],
 
103
 
104
 
105
  with gr.Blocks(title=APP_TITLE) as demo:
106
+ gr.Markdown(f"# {APP_TITLE}\nOpenAIでOCR/構造化/要約→統合→スコア(匿名化・保存なし)")
107
 
108
  with gr.Row():
109
+ # ★ Gradio v4 の仕様に合わせて 'filepath' を使用('file' は不可)
110
  in_files = gr.Files(
111
  label="レジュメ類 (PDF/画像/Word/テキスト) 複数可",
112
  file_count="multiple",
 
122
  out_json = gr.Code(label="統合出力 (JSON)")
123
 
124
  with gr.Tab("抽出スキル"):
125
+ # ★ GradioのJSONスキーマ推論バグを避けるため Code に統一
126
  out_skills = gr.Code(label="スキル一覧 (JSON)")
127
 
128
  with gr.Tab("品質スコア"):
 
141
 
142
 
143
  if __name__ == "__main__":
144
+ # ローカル到達不可環境でも動くように share=True を明示
145
  demo.launch(share=True, server_name="0.0.0.0", server_port=7860)