Skip to content

Commit be66e73

Browse files
authored
Merge pull request #79 from jphacks/develop
mainへのマージ
2 parents d6bbe7a + 5aeff22 commit be66e73

15 files changed

Lines changed: 1800 additions & 353 deletions

back/app/main.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# main.py
21
from fastapi import FastAPI
32
from app.router import animation
43
from fastapi.middleware.cors import CORSMiddleware
@@ -12,7 +11,12 @@
1211
# CORS(Vercelなどからのアクセス許可)
1312
app.add_middleware(
1413
CORSMiddleware,
15-
allow_origins=["https://ng2501.vercel.app/","http://localhost:3000"], # 本番ではドメインを限定
14+
# 末尾のスラッシュなしのオリジンを明記(または両方併記)
15+
allow_origins=[
16+
"https://ng2501.vercel.app",
17+
"https://ng2501.vercel.app/",
18+
"http://localhost:3000"
19+
],
1620
allow_credentials=True,
1721
allow_methods=["*"],
1822
allow_headers=["*"],
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# from __future__ import annotations
2+
3+
# from typing import Optional
4+
5+
# import numpy as np
6+
# import torch
7+
# from transformers import AutoModel, AutoTokenizer
8+
9+
10+
# def _mean_pool(
11+
# last_hidden_state: torch.Tensor, attention_mask: torch.Tensor
12+
# ) -> torch.Tensor:
13+
# """
14+
# アテンションマスクを考慮した平均プーリングを行う。
15+
# """
16+
# mask = attention_mask.unsqueeze(-1).type_as(last_hidden_state)
17+
# summed = (last_hidden_state * mask).sum(dim=1)
18+
# counts = mask.sum(dim=1).clamp(min=1e-9)
19+
# return summed / counts
20+
21+
22+
# class TemplateEncoder:
23+
# """
24+
# テンプレートテキストを埋め込みベクトルへ変換するエンコーダ。
25+
# """
26+
27+
# DEFAULT_MODEL_NAME = "cl-nagoya/ruri-v3-30m"
28+
29+
# def __init__(self, model_name: Optional[str] = None, *, device: str = "cpu") -> None:
30+
# self.model_name = model_name or self.DEFAULT_MODEL_NAME
31+
# self.device = device
32+
33+
# self.tokenizer = AutoTokenizer.from_pretrained(self.model_name)
34+
# self.model = AutoModel.from_pretrained(self.model_name).to(self.device)
35+
# self.model.eval()
36+
37+
# @torch.no_grad()
38+
# def encode(self, text: str, *, max_length: int = 256) -> np.ndarray:
39+
# """
40+
# テキストをエンコードし、正規化済みの文ベクトルを返す。
41+
# """
42+
# encoded = self.tokenizer(
43+
# [text],
44+
# padding=True,
45+
# truncation=True,
46+
# max_length=max_length,
47+
# return_tensors="pt",
48+
# )
49+
# encoded = {k: v.to(self.device) for k, v in encoded.items()}
50+
# model_output = self.model(**encoded)
51+
# sentence_vector = _mean_pool(
52+
# model_output.last_hidden_state, encoded["attention_mask"]
53+
# )
54+
# sentence_vector = torch.nn.functional.normalize(sentence_vector, p=2, dim=1)
55+
# return sentence_vector.cpu().numpy()[0]
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# from __future__ import annotations
2+
3+
# import json
4+
# import re
5+
# from dataclasses import dataclass
6+
# from pathlib import Path
7+
# from typing import Dict, List, Optional, Pattern, Tuple
8+
9+
# import numpy as np
10+
11+
# from back.app.resources.template_encoder import TemplateEncoder
12+
13+
14+
# @dataclass(frozen=True)
15+
# class TemplateEntry:
16+
# """
17+
# テンプレートリソースに保存される1件分のエントリ。
18+
# """
19+
20+
# template_id: str
21+
# theme: str
22+
# code: str
23+
24+
25+
# class TemplateRetriever:
26+
# """
27+
# テンプレート情報の読み込みと埋め込み検索を担当するクラス。
28+
# """
29+
30+
# MODEL_NAME = "cl-nagoya/ruri-v3-30m"
31+
32+
# def __init__(self, resource_dir: Path) -> None:
33+
# """
34+
# テンプレート情報と埋め込みを読み込み、埋め込みモデルを初期化する。
35+
# """
36+
# self.resource_dir = resource_dir
37+
# self.resource_dir.mkdir(parents=True, exist_ok=True)
38+
# self.embeddings_path = self.resource_dir / "theme_embeddings.npy"
39+
# self.templates_path = self.resource_dir / "manim_template.jsonl"
40+
41+
# self.templates: List[TemplateEntry] = self._load_templates()
42+
# self.embeddings: np.ndarray = self._load_embeddings()
43+
# if self.embeddings.shape[0] != len(self.templates):
44+
# raise ValueError("Template count and embedding rows mismatch.")
45+
46+
# self._template_by_id: Dict[str, TemplateEntry] = {
47+
# entry.template_id: entry for entry in self.templates
48+
# }
49+
# self._sequence_pattern_cache: Dict[str, Pattern[str]] = {}
50+
51+
# self.encoder = TemplateEncoder(self.MODEL_NAME)
52+
53+
# def _load_templates(self) -> List[TemplateEntry]:
54+
# """
55+
# JSONL からテンプレート情報を読み込む。ファイルが無ければ空リストを返す。
56+
# """
57+
# entries: List[TemplateEntry] = []
58+
# if not self.templates_path.exists():
59+
# return entries
60+
# with self.templates_path.open("r", encoding="utf-8") as fh:
61+
# for line in fh:
62+
# payload = line.strip()
63+
# if not payload:
64+
# continue
65+
# record = json.loads(payload)
66+
# entries.append(
67+
# TemplateEntry(
68+
# template_id=record["id"],
69+
# theme=record["theme"],
70+
# code=record.get("code", ""),
71+
# )
72+
# )
73+
# return entries
74+
75+
# def _load_embeddings(self) -> np.ndarray:
76+
# """
77+
# 埋め込み行列を取得し、各行が単位ベクトルになるよう正規化する。
78+
# """
79+
# if not self.embeddings_path.exists():
80+
# return np.empty((0, 0), dtype=np.float32)
81+
# matrix = np.load(self.embeddings_path)
82+
# if matrix.size == 0:
83+
# return matrix
84+
# norms = np.linalg.norm(matrix, axis=1, keepdims=True)
85+
# norms[norms == 0] = 1e-9
86+
# return matrix / norms
87+
88+
# def encode(self, text: str, *, max_length: int = 256) -> np.ndarray:
89+
# """
90+
# テキストをエンコードし、正規化済みの文ベクトルを返す。
91+
# """
92+
# return self.encoder.encode(text, max_length=max_length)
93+
94+
# def allocate_template_id(
95+
# self, template_id: Optional[str] = None, *, prefix: str = "code"
96+
# ) -> str:
97+
# """
98+
# 新しいテンプレートIDを取得する。テンプレートID指定時は重複チェックのみ行う。
99+
# """
100+
# if template_id:
101+
# if template_id in self._template_by_id:
102+
# raise ValueError("指定された template_id は既に存在します。")
103+
# return template_id
104+
# next_seq = self._compute_next_sequence(prefix)
105+
# return f"{prefix}{next_seq}"
106+
107+
# def append_entry(
108+
# self, entry: TemplateEntry, embedding: np.ndarray, *, persist: bool = False
109+
# ) -> None:
110+
# """
111+
# 新しいテンプレートと埋め込みを登録し、必要に応じて永続化する。
112+
# """
113+
# if embedding.ndim != 1:
114+
# raise ValueError("Embedding must be a 1-D vector.")
115+
# norm = np.linalg.norm(embedding)
116+
# if norm == 0:
117+
# raise ValueError("Embedding norm is zero.")
118+
# normalized = (embedding / norm).astype(np.float32)
119+
120+
# if entry.template_id in self._template_by_id:
121+
# raise ValueError(
122+
# f"template_id '{entry.template_id}' is already registered."
123+
# )
124+
125+
# if self.embeddings.size == 0:
126+
# self.embeddings = normalized[None, :]
127+
# else:
128+
# self.embeddings = np.vstack([self.embeddings, normalized])
129+
130+
# self.templates.append(entry)
131+
# self._template_by_id[entry.template_id] = entry
132+
133+
# if persist:
134+
# self._append_template_to_disk(entry)
135+
# self._save_embeddings()
136+
137+
# def get_by_id(self, template_id: str) -> Optional[TemplateEntry]:
138+
# return self._template_by_id.get(template_id)
139+
140+
# def topk(self, query: np.ndarray, *, k: int = 3) -> List[Tuple[int, float]]:
141+
# """
142+
# 類似度の高い順に上位 k 件の (インデックス, スコア) を返す。
143+
# """
144+
# if self.embeddings.size == 0:
145+
# return []
146+
# scores = self.embeddings @ query
147+
# ranked = np.argsort(scores)[::-1]
148+
# return [(int(idx), float(scores[idx])) for idx in ranked[:k]]
149+
150+
# def remove_latest(self, *, persist: bool = False) -> TemplateEntry:
151+
# """
152+
# 最後に追加されたテンプレートを削除し、必要に応じて永続化する。
153+
# """
154+
# if not self.templates:
155+
# raise ValueError("テンプレートが登録されていません。")
156+
157+
# removed_entry = self.templates.pop()
158+
# self._template_by_id.pop(removed_entry.template_id, None)
159+
160+
# if self.embeddings.size == 0 or self.embeddings.shape[0] <= 1:
161+
# self.embeddings = np.empty((0, 0), dtype=np.float32)
162+
# else:
163+
# self.embeddings = self.embeddings[:-1, :]
164+
165+
# if persist:
166+
# self._write_templates_file()
167+
# self._save_embeddings()
168+
169+
# return removed_entry
170+
171+
# def _compute_next_sequence(self, prefix: str) -> int:
172+
# pattern = self._get_id_pattern(prefix)
173+
# max_seq = 0
174+
# for entry in self.templates:
175+
# match = pattern.match(entry.template_id)
176+
# if match:
177+
# max_seq = max(max_seq, int(match.group(1)))
178+
# return max_seq + 1
179+
180+
# def _append_template_to_disk(self, entry: TemplateEntry) -> None:
181+
# payload = {
182+
# "id": entry.template_id,
183+
# "theme": entry.theme,
184+
# "code": entry.code,
185+
# }
186+
# with self.templates_path.open("a", encoding="utf-8") as fh:
187+
# fh.write(json.dumps(payload, ensure_ascii=False))
188+
# fh.write("\n")
189+
190+
# def _write_templates_file(self) -> None:
191+
# if not self.templates:
192+
# self.templates_path.write_text("", encoding="utf-8")
193+
# return
194+
195+
# lines = []
196+
# for entry in self.templates:
197+
# payload = {
198+
# "id": entry.template_id,
199+
# "theme": entry.theme,
200+
# "code": entry.code,
201+
# }
202+
# lines.append(json.dumps(payload, ensure_ascii=False))
203+
# self.templates_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
204+
205+
# def _save_embeddings(self) -> None:
206+
# np.save(self.embeddings_path, self.embeddings)
207+
208+
# def _get_id_pattern(self, prefix: str) -> Pattern[str]:
209+
# pattern = self._sequence_pattern_cache.get(prefix)
210+
# if pattern is None:
211+
# pattern = re.compile(rf"^{re.escape(prefix)}(\d+)$")
212+
# self._sequence_pattern_cache[prefix] = pattern
213+
# return pattern

0 commit comments

Comments
 (0)