ikram98ai commited on
Commit
52b6de3
·
1 Parent(s): 15bd23d

adding proper cost estimation and initial data logs

Browse files
Files changed (3) hide show
  1. requirements.txt +373 -0
  2. src/app.py +17 -3
  3. src/pipeline.py +36 -20
requirements.txt ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file was autogenerated by uv via the following command:
2
+ # uv pip compile pyproject.toml -o requirements.txt
3
+ aiofiles==24.1.0
4
+ # via gradio
5
+ aiohappyeyeballs==2.6.1
6
+ # via aiohttp
7
+ aiohttp==3.13.2
8
+ # via langchain-community
9
+ aiosignal==1.4.0
10
+ # via aiohttp
11
+ annotated-doc==0.0.4
12
+ # via fastapi
13
+ annotated-types==0.7.0
14
+ # via pydantic
15
+ anthropic==0.75.0
16
+ # via langchain-anthropic
17
+ anyio==4.12.0
18
+ # via
19
+ # anthropic
20
+ # gradio
21
+ # httpx
22
+ # openai
23
+ # starlette
24
+ attrs==25.4.0
25
+ # via aiohttp
26
+ audioop-lts==0.2.2
27
+ # via gradio
28
+ brotli==1.2.0
29
+ # via gradio
30
+ cachetools==6.2.2
31
+ # via google-auth
32
+ certifi==2025.11.12
33
+ # via
34
+ # httpcore
35
+ # httpx
36
+ # requests
37
+ charset-normalizer==3.4.4
38
+ # via requests
39
+ click==8.3.1
40
+ # via
41
+ # typer
42
+ # typer-slim
43
+ # uvicorn
44
+ dataclasses-json==0.6.7
45
+ # via langchain-community
46
+ distro==1.9.0
47
+ # via
48
+ # anthropic
49
+ # openai
50
+ docstring-parser==0.17.0
51
+ # via anthropic
52
+ fastapi==0.123.4
53
+ # via gradio
54
+ ffmpy==1.0.0
55
+ # via gradio
56
+ filelock==3.20.0
57
+ # via huggingface-hub
58
+ filetype==1.2.0
59
+ # via langchain-google-genai
60
+ frozenlist==1.8.0
61
+ # via
62
+ # aiohttp
63
+ # aiosignal
64
+ fsspec==2025.10.0
65
+ # via
66
+ # gradio-client
67
+ # huggingface-hub
68
+ google-ai-generativelanguage==0.9.0
69
+ # via langchain-google-genai
70
+ google-api-core==2.28.1
71
+ # via google-ai-generativelanguage
72
+ google-auth==2.43.0
73
+ # via
74
+ # google-ai-generativelanguage
75
+ # google-api-core
76
+ googleapis-common-protos==1.72.0
77
+ # via
78
+ # google-api-core
79
+ # grpcio-status
80
+ gradio==6.0.2
81
+ # via litigation-research (pyproject.toml)
82
+ gradio-client==2.0.1
83
+ # via gradio
84
+ greenlet==3.2.4
85
+ # via sqlalchemy
86
+ groovy==0.1.2
87
+ # via gradio
88
+ grpcio==1.76.0
89
+ # via
90
+ # google-ai-generativelanguage
91
+ # google-api-core
92
+ # grpcio-status
93
+ grpcio-status==1.76.0
94
+ # via google-api-core
95
+ h11==0.16.0
96
+ # via
97
+ # httpcore
98
+ # uvicorn
99
+ hf-xet==1.2.0
100
+ # via huggingface-hub
101
+ httpcore==1.0.9
102
+ # via httpx
103
+ httpx==0.28.1
104
+ # via
105
+ # anthropic
106
+ # gradio
107
+ # gradio-client
108
+ # huggingface-hub
109
+ # langgraph-sdk
110
+ # langsmith
111
+ # openai
112
+ # safehttpx
113
+ httpx-sse==0.4.3
114
+ # via langchain-community
115
+ huggingface-hub==1.1.7
116
+ # via
117
+ # gradio
118
+ # gradio-client
119
+ idna==3.11
120
+ # via
121
+ # anyio
122
+ # httpx
123
+ # requests
124
+ # yarl
125
+ jinja2==3.1.6
126
+ # via gradio
127
+ jiter==0.12.0
128
+ # via
129
+ # anthropic
130
+ # openai
131
+ jsonpatch==1.33
132
+ # via langchain-core
133
+ jsonpointer==3.0.0
134
+ # via jsonpatch
135
+ langchain==1.1.0
136
+ # via litigation-research (pyproject.toml)
137
+ langchain-anthropic==1.2.0
138
+ # via litigation-research (pyproject.toml)
139
+ langchain-classic==1.0.0
140
+ # via langchain-community
141
+ langchain-community==0.4.1
142
+ # via litigation-research (pyproject.toml)
143
+ langchain-core==1.1.0
144
+ # via
145
+ # langchain
146
+ # langchain-anthropic
147
+ # langchain-classic
148
+ # langchain-community
149
+ # langchain-google-genai
150
+ # langchain-openai
151
+ # langchain-text-splitters
152
+ # langgraph
153
+ # langgraph-checkpoint
154
+ # langgraph-prebuilt
155
+ langchain-google-genai==3.2.0
156
+ # via litigation-research (pyproject.toml)
157
+ langchain-openai==1.1.0
158
+ # via litigation-research (pyproject.toml)
159
+ langchain-text-splitters==1.0.0
160
+ # via langchain-classic
161
+ langgraph==1.0.4
162
+ # via langchain
163
+ langgraph-checkpoint==3.0.1
164
+ # via
165
+ # langgraph
166
+ # langgraph-prebuilt
167
+ langgraph-prebuilt==1.0.5
168
+ # via langgraph
169
+ langgraph-sdk==0.2.12
170
+ # via langgraph
171
+ langsmith==0.4.50
172
+ # via
173
+ # langchain-classic
174
+ # langchain-community
175
+ # langchain-core
176
+ markdown-it-py==4.0.0
177
+ # via rich
178
+ markupsafe==3.0.3
179
+ # via
180
+ # gradio
181
+ # jinja2
182
+ marshmallow==3.26.1
183
+ # via dataclasses-json
184
+ mdurl==0.1.2
185
+ # via markdown-it-py
186
+ multidict==6.7.0
187
+ # via
188
+ # aiohttp
189
+ # yarl
190
+ mypy-extensions==1.1.0
191
+ # via typing-inspect
192
+ numpy==2.3.5
193
+ # via
194
+ # gradio
195
+ # langchain-community
196
+ # pandas
197
+ openai==2.8.1
198
+ # via langchain-openai
199
+ orjson==3.11.4
200
+ # via
201
+ # gradio
202
+ # langgraph-sdk
203
+ # langsmith
204
+ ormsgpack==1.12.0
205
+ # via langgraph-checkpoint
206
+ packaging==25.0
207
+ # via
208
+ # gradio
209
+ # gradio-client
210
+ # huggingface-hub
211
+ # langchain-core
212
+ # langsmith
213
+ # marshmallow
214
+ pandas==2.3.3
215
+ # via
216
+ # litigation-research (pyproject.toml)
217
+ # gradio
218
+ pillow==12.0.0
219
+ # via gradio
220
+ propcache==0.4.1
221
+ # via
222
+ # aiohttp
223
+ # yarl
224
+ proto-plus==1.26.1
225
+ # via
226
+ # google-ai-generativelanguage
227
+ # google-api-core
228
+ protobuf==6.33.1
229
+ # via
230
+ # google-ai-generativelanguage
231
+ # google-api-core
232
+ # googleapis-common-protos
233
+ # grpcio-status
234
+ # proto-plus
235
+ psycopg2-binary==2.9.11
236
+ # via litigation-research (pyproject.toml)
237
+ pyasn1==0.6.1
238
+ # via
239
+ # pyasn1-modules
240
+ # rsa
241
+ pyasn1-modules==0.4.2
242
+ # via google-auth
243
+ pydantic==2.12.4
244
+ # via
245
+ # anthropic
246
+ # fastapi
247
+ # gradio
248
+ # langchain
249
+ # langchain-anthropic
250
+ # langchain-classic
251
+ # langchain-core
252
+ # langchain-google-genai
253
+ # langgraph
254
+ # langsmith
255
+ # openai
256
+ # pydantic-settings
257
+ pydantic-core==2.41.5
258
+ # via pydantic
259
+ pydantic-settings==2.12.0
260
+ # via langchain-community
261
+ pydub==0.25.1
262
+ # via gradio
263
+ pygments==2.19.2
264
+ # via rich
265
+ pypdf==6.4.0
266
+ # via litigation-research (pyproject.toml)
267
+ python-dateutil==2.9.0.post0
268
+ # via pandas
269
+ python-dotenv==1.2.1
270
+ # via
271
+ # litigation-research (pyproject.toml)
272
+ # pydantic-settings
273
+ python-multipart==0.0.20
274
+ # via gradio
275
+ pytz==2025.2
276
+ # via pandas
277
+ pyyaml==6.0.3
278
+ # via
279
+ # gradio
280
+ # huggingface-hub
281
+ # langchain-classic
282
+ # langchain-community
283
+ # langchain-core
284
+ regex==2025.11.3
285
+ # via tiktoken
286
+ requests==2.32.5
287
+ # via
288
+ # google-api-core
289
+ # langchain-classic
290
+ # langchain-community
291
+ # langsmith
292
+ # requests-toolbelt
293
+ # tiktoken
294
+ requests-toolbelt==1.0.0
295
+ # via langsmith
296
+ rich==14.2.0
297
+ # via typer
298
+ rsa==4.9.1
299
+ # via google-auth
300
+ safehttpx==0.1.7
301
+ # via gradio
302
+ semantic-version==2.10.0
303
+ # via gradio
304
+ shellingham==1.5.4
305
+ # via
306
+ # huggingface-hub
307
+ # typer
308
+ six==1.17.0
309
+ # via python-dateutil
310
+ sniffio==1.3.1
311
+ # via
312
+ # anthropic
313
+ # openai
314
+ sqlalchemy==2.0.44
315
+ # via
316
+ # litigation-research (pyproject.toml)
317
+ # langchain-classic
318
+ # langchain-community
319
+ starlette==0.50.0
320
+ # via
321
+ # fastapi
322
+ # gradio
323
+ tenacity==9.1.2
324
+ # via
325
+ # langchain-community
326
+ # langchain-core
327
+ tiktoken==0.12.0
328
+ # via langchain-openai
329
+ tomlkit==0.13.3
330
+ # via gradio
331
+ tqdm==4.67.1
332
+ # via
333
+ # huggingface-hub
334
+ # openai
335
+ typer==0.20.0
336
+ # via gradio
337
+ typer-slim==0.20.0
338
+ # via huggingface-hub
339
+ typing-extensions==4.15.0
340
+ # via
341
+ # anthropic
342
+ # fastapi
343
+ # gradio
344
+ # gradio-client
345
+ # grpcio
346
+ # huggingface-hub
347
+ # langchain-core
348
+ # openai
349
+ # pydantic
350
+ # pydantic-core
351
+ # sqlalchemy
352
+ # typer
353
+ # typer-slim
354
+ # typing-inspect
355
+ # typing-inspection
356
+ typing-inspect==0.9.0
357
+ # via dataclasses-json
358
+ typing-inspection==0.4.2
359
+ # via
360
+ # pydantic
361
+ # pydantic-settings
362
+ tzdata==2025.2
363
+ # via pandas
364
+ urllib3==2.5.0
365
+ # via requests
366
+ uvicorn==0.38.0
367
+ # via gradio
368
+ xxhash==3.6.0
369
+ # via langgraph
370
+ yarl==1.22.0
371
+ # via aiohttp
372
+ zstandard==0.25.0
373
+ # via langsmith
src/app.py CHANGED
@@ -1,6 +1,7 @@
1
  import gradio as gr
2
  import pandas as pd
3
- from pipeline import LitigationPipeline
 
4
 
5
 
6
  # --- DEFAULT PROMPTS ---
@@ -43,6 +44,17 @@ def run_pipeline(files, api_key, model, context_limit, retries, sum_prompt, ver_
43
 
44
  return status_msg, logs_df
45
 
 
 
 
 
 
 
 
 
 
 
 
46
  # --- GRADIO LAYOUT ---
47
 
48
  with gr.Blocks(title="Pharma Litigation AI Researcher") as demo:
@@ -62,7 +74,7 @@ with gr.Blocks(title="Pharma Litigation AI Researcher") as demo:
62
  gr.Markdown("---")
63
  gr.Markdown("### ⚙️ Configuration")
64
  api_key_input = gr.Textbox(label="API Key", type="password", placeholder="sk-...")
65
- model_selector = gr.Dropdown(["google_genai:gemini-2.5-flash", "openai:gpt-4o", "anthropic:claude-sonnet-4-5-20250929"], label="Model", value="google_genai:gemini-2.5-flash")
66
  context_limit = gr.Number(label="Reset Context After (N docs)", value=5, precision=0)
67
  retry_limit = gr.Slider(minimum=0, maximum=5, value=2, step=1, label="Max Verification Retries")
68
 
@@ -73,7 +85,8 @@ with gr.Blocks(title="Pharma Litigation AI Researcher") as demo:
73
  with gr.Tabs():
74
  with gr.TabItem("📊 Live Execution Logs"):
75
  status_output = gr.Markdown("Waiting for input...")
76
- log_table = gr.Dataframe(label="Processing Logs (PostgreSQL)", headers=["filename", "status", "score", "feedback"])
 
77
 
78
 
79
 
@@ -91,6 +104,7 @@ with gr.Blocks(title="Pharma Litigation AI Researcher") as demo:
91
  ],
92
  outputs=[status_output, log_table]
93
  )
 
94
 
95
  if __name__ == "__main__":
96
  demo.launch()
 
1
  import gradio as gr
2
  import pandas as pd
3
+ from pipeline import LitigationPipeline, MODELS
4
+ from db import init_db, DocumentLog
5
 
6
 
7
  # --- DEFAULT PROMPTS ---
 
44
 
45
  return status_msg, logs_df
46
 
47
+ def get_initial_logs():
48
+ """Fetch initial logs to populate the UI."""
49
+ db_session = init_db()
50
+ query = db_session.query(DocumentLog).order_by(DocumentLog.timestamp.desc()).limit(50)
51
+ df = pd.read_sql(query.statement, db_session.bind)
52
+ # Ensure columns match the expected headers in the UI
53
+ df_display = df.rename(columns={
54
+ "verification_score": "score",
55
+ "verifier_feedback": "feedback"
56
+ })
57
+ return df_display
58
  # --- GRADIO LAYOUT ---
59
 
60
  with gr.Blocks(title="Pharma Litigation AI Researcher") as demo:
 
74
  gr.Markdown("---")
75
  gr.Markdown("### ⚙️ Configuration")
76
  api_key_input = gr.Textbox(label="API Key", type="password", placeholder="sk-...")
77
+ model_selector = gr.Dropdown(list(MODELS.keys()), label="Model", value=list(MODELS.keys())[0])
78
  context_limit = gr.Number(label="Reset Context After (N docs)", value=5, precision=0)
79
  retry_limit = gr.Slider(minimum=0, maximum=5, value=2, step=1, label="Max Verification Retries")
80
 
 
85
  with gr.Tabs():
86
  with gr.TabItem("📊 Live Execution Logs"):
87
  status_output = gr.Markdown("Waiting for input...")
88
+ log_table = gr.Dataframe(label="Processing Logs (PostgreSQL)",
89
+ headers=["filename", "status", "score", "feedback"])
90
 
91
 
92
 
 
104
  ],
105
  outputs=[status_output, log_table]
106
  )
107
+ demo.load(get_initial_logs, None, log_table)
108
 
109
  if __name__ == "__main__":
110
  demo.launch()
src/pipeline.py CHANGED
@@ -1,22 +1,34 @@
1
  from langchain_core.prompts import ChatPromptTemplate
2
- from langchain_core.output_parsers import StrOutputParser
3
  from langchain.chat_models import init_chat_model
4
  from langchain_community.document_loaders import PyPDFLoader
 
5
  import pandas as pd
6
  import os
7
  from db import init_db, DocumentLog
8
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  class LitigationPipeline:
10
- def __init__(self, api_key, model_provider, batch_context_limit):
11
  self.api_key = api_key
12
- self.provider = model_provider
13
  self.context_limit = int(batch_context_limit)
14
  self.processed_count = 0
15
  self.db_session = init_db()
16
 
17
  # Initialize LLM
18
  try:
19
- self.llm = init_chat_model(model=model_provider, api_key=api_key)
20
  except Exception as e:
21
  raise ValueError("Unsupported Model: " + str(e))
22
 
@@ -26,7 +38,7 @@ class LitigationPipeline:
26
  # or clearing conversation buffers.
27
  print(f"--- SYSTEM: Context Reset Triggered after {self.processed_count} docs ---")
28
  # For this prototype, we re-initialize the LLM client to ensure no cached state remains
29
- self.llm = init_chat_model(model=self.provider, api_key=self.api_key)
30
 
31
  def extract_text(self, file_path):
32
  loader = PyPDFLoader(file_path)
@@ -43,8 +55,12 @@ class LitigationPipeline:
43
  ("system", system_instruction),
44
  ("user", f"Analyze this legal text:\n{text[:50000]}...") # Truncate for prototype safety
45
  ])
46
- chain = prompt | self.llm | StrOutputParser()
47
- return chain.invoke({})
 
 
 
 
48
 
49
  def verify(self, summary, original_text, verifier_prompt):
50
  """Critiques the summary. Returns (score, feedback)."""
@@ -52,19 +68,19 @@ class LitigationPipeline:
52
  ("system", verifier_prompt),
53
  ("user", f"Original Text (Excerpt): {original_text[:5000]}...\n\nSummary to Verify:\n{summary}")
54
  ])
55
- chain = prompt | self.llm | StrOutputParser()
56
  result = chain.invoke({})
57
-
 
58
  # Simple parsing for prototype: Expecting "SCORE: X/10\nFEEDBACK: ..."
59
  try:
60
- score_line = [line for line in result.split('\n') if "SCORE:" in line][0]
61
- score = float(score_line.split(":")[1].strip().split("/")[0])
62
- feedback = result.split("FEEDBACK:")[1].strip()
63
  except:
64
  score = 0
65
  feedback = "Parsing Error in Verifier Output"
66
 
67
- return score, feedback
68
 
69
  def process_batch(self, file_paths, summary_prompt, verifier_prompt, retry_limit):
70
  results = []
@@ -84,7 +100,7 @@ class LitigationPipeline:
84
  final_score = 0
85
 
86
  extracted_text = ""
87
-
88
  try:
89
  extracted_text = self.extract_text(file_path)
90
 
@@ -93,11 +109,11 @@ class LitigationPipeline:
93
  print(f"Processing {file_name}: Attempt {attempts + 1}")
94
 
95
  # 1. Summarize
96
- current_summary = self.summarize(extracted_text, summary_prompt, feedback=current_feedback if attempts > 0 else None)
97
-
98
  # 2. Verify
99
- final_score, current_feedback = self.verify(current_summary, extracted_text, verifier_prompt)
100
-
101
  # 3. Decision
102
  if final_score >= 8.0: # Threshold for success
103
  success = True
@@ -118,8 +134,8 @@ class LitigationPipeline:
118
  verification_score=final_score,
119
  verifier_feedback=current_feedback,
120
  final_summary=current_summary,
121
- model_used=self.provider,
122
- cost_estimate=0.01 * (attempts + 1) # Placeholder logic
123
  )
124
  self.db_session.add(log_entry)
125
  self.db_session.commit()
 
1
  from langchain_core.prompts import ChatPromptTemplate
 
2
  from langchain.chat_models import init_chat_model
3
  from langchain_community.document_loaders import PyPDFLoader
4
+ from pydantic import BaseModel
5
  import pandas as pd
6
  import os
7
  from db import init_db, DocumentLog
8
 
9
+ MODELS = {
10
+ "Google Gemini 2.5 Flash":{"model_id":"google_genai:gemini-2.5-flash", "cost":0.00001},
11
+ "OpenAI GPT-4o":{"model_id":"openai:gpt-4o", "cost":0.00003},
12
+ "Claude Sonnet 4.5":{"model_id":"anthropic:claude-sonnet-4-5-20250929", "cost":0.00002},
13
+ }
14
+
15
+
16
+ class VerifierResponse(BaseModel):
17
+ score: float
18
+ feedback: str
19
+
20
+
21
  class LitigationPipeline:
22
+ def __init__(self, api_key, model, batch_context_limit):
23
  self.api_key = api_key
24
+ self.model = model
25
  self.context_limit = int(batch_context_limit)
26
  self.processed_count = 0
27
  self.db_session = init_db()
28
 
29
  # Initialize LLM
30
  try:
31
+ self.llm = init_chat_model(model=MODELS[self.model]['model_id'], api_key=api_key)
32
  except Exception as e:
33
  raise ValueError("Unsupported Model: " + str(e))
34
 
 
38
  # or clearing conversation buffers.
39
  print(f"--- SYSTEM: Context Reset Triggered after {self.processed_count} docs ---")
40
  # For this prototype, we re-initialize the LLM client to ensure no cached state remains
41
+ self.llm = init_chat_model(model=self.model, api_key=self.api_key)
42
 
43
  def extract_text(self, file_path):
44
  loader = PyPDFLoader(file_path)
 
55
  ("system", system_instruction),
56
  ("user", f"Analyze this legal text:\n{text[:50000]}...") # Truncate for prototype safety
57
  ])
58
+ chain = prompt | self.llm
59
+
60
+ result = chain.invoke({})
61
+ tokens = result.usage_metadata.get("total_tokens", 0)
62
+ cost = tokens * MODELS[self.model]['cost']
63
+ return result.content, cost
64
 
65
  def verify(self, summary, original_text, verifier_prompt):
66
  """Critiques the summary. Returns (score, feedback)."""
 
68
  ("system", verifier_prompt),
69
  ("user", f"Original Text (Excerpt): {original_text[:5000]}...\n\nSummary to Verify:\n{summary}")
70
  ])
71
+ chain = prompt | self.llm.with_structured_output(VerifierResponse)
72
  result = chain.invoke({})
73
+ cost = len(result.feedback.split(" ")) * 0.7 * MODELS[self.model]['cost']
74
+
75
  # Simple parsing for prototype: Expecting "SCORE: X/10\nFEEDBACK: ..."
76
  try:
77
+ score = result.score
78
+ feedback = result.feedback
 
79
  except:
80
  score = 0
81
  feedback = "Parsing Error in Verifier Output"
82
 
83
+ return score, feedback, cost
84
 
85
  def process_batch(self, file_paths, summary_prompt, verifier_prompt, retry_limit):
86
  results = []
 
100
  final_score = 0
101
 
102
  extracted_text = ""
103
+ total_cost=0
104
  try:
105
  extracted_text = self.extract_text(file_path)
106
 
 
109
  print(f"Processing {file_name}: Attempt {attempts + 1}")
110
 
111
  # 1. Summarize
112
+ current_summary, summary_cost = self.summarize(extracted_text, summary_prompt, feedback=current_feedback if attempts > 0 else None)
113
+ total_cost += summary_cost
114
  # 2. Verify
115
+ final_score, current_feedback, verifier_cost = self.verify(current_summary, extracted_text, verifier_prompt)
116
+ total_cost += verifier_cost
117
  # 3. Decision
118
  if final_score >= 8.0: # Threshold for success
119
  success = True
 
134
  verification_score=final_score,
135
  verifier_feedback=current_feedback,
136
  final_summary=current_summary,
137
+ model_used=self.model,
138
+ cost_estimate=total_cost
139
  )
140
  self.db_session.add(log_entry)
141
  self.db_session.commit()