Added docker support
This commit is contained in:
12
.dockerignore
Normal file
12
.dockerignore
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
.git
|
||||||
|
.venv
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
runtime/
|
||||||
|
debug/
|
||||||
|
images/
|
||||||
|
input.*
|
||||||
|
audio.*
|
||||||
|
video.*
|
||||||
|
output.*
|
||||||
|
*.json
|
||||||
36
Dockerfile
Normal file
36
Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
ARG GPU=nvidia
|
||||||
|
|
||||||
|
FROM nvidia/cuda:13.0.2-cudnn-runtime-ubuntu24.04 AS base-nvidia
|
||||||
|
FROM rocm/dev-ubuntu-24.04:7.2.1-complete AS base-amd
|
||||||
|
|
||||||
|
FROM base-${GPU} AS app
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive \
|
||||||
|
PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1 \
|
||||||
|
XDG_CACHE_HOME=/tmp/sumka-cache
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates curl ffmpeg fonts-dejavu-core \
|
||||||
|
python3 python3-pip python3-venv \
|
||||||
|
&& curl -fsSL -o /tmp/chrome.deb \
|
||||||
|
https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
|
||||||
|
&& apt-get install -y /tmp/chrome.deb \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /tmp/chrome.deb
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY requirements.txt requirements-nvidia.txt requirements-amd.txt ./
|
||||||
|
ARG GPU
|
||||||
|
RUN python3 -m venv /opt/venv \
|
||||||
|
&& /opt/venv/bin/pip install --no-cache-dir --upgrade pip \
|
||||||
|
&& /opt/venv/bin/pip install --no-cache-dir -r requirements-${GPU}.txt
|
||||||
|
|
||||||
|
ENV PATH=/opt/venv/bin:$PATH
|
||||||
|
|
||||||
|
COPY . ./
|
||||||
|
WORKDIR /work
|
||||||
|
|
||||||
|
ENTRYPOINT ["python3", "/app/main.py"]
|
||||||
35
README.md
35
README.md
@@ -15,7 +15,36 @@
|
|||||||
|
|
||||||
## Установка (Docker)
|
## Установка (Docker)
|
||||||
|
|
||||||
TODO
|
Образы собираются из одного Dockerfile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build --build-arg GPU=nvidia -t sumka:nvidia .
|
||||||
|
docker build --build-arg GPU=amd -t sumka:amd .
|
||||||
|
```
|
||||||
|
|
||||||
|
Для NVIDIA нужен настроенный NVIDIA Container Toolkit. Для AMD на хосте должен
|
||||||
|
быть установлен совместимый ROCm. Рабочая директория монтируется в `/work`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WORK_DIR=$(mktemp -d)
|
||||||
|
cp input.mkv "$WORK_DIR/"
|
||||||
|
|
||||||
|
# nvidia
|
||||||
|
docker run --rm --gpus all -e AI_API_KEY \
|
||||||
|
--mount type=volume,src=sumka-whisper-cache,dst=/root/.cache/whisper \
|
||||||
|
--mount type=bind,src="$WORK_DIR",dst=/work \
|
||||||
|
--tmpfs /tmp:rw,size=8g sumka:nvidia
|
||||||
|
|
||||||
|
# amd
|
||||||
|
docker run --rm --device=/dev/kfd --device=/dev/dri --group-add video \
|
||||||
|
--security-opt seccomp=unconfined -e AI_API_KEY \
|
||||||
|
--mount type=volume,src=sumka-whisper-cache,dst=/root/.cache/whisper \
|
||||||
|
--mount type=bind,src="$WORK_DIR",dst=/work \
|
||||||
|
--tmpfs /tmp:rw,size=8g sumka:amd
|
||||||
|
```
|
||||||
|
|
||||||
|
Все входные, промежуточные и итоговые файлы находятся в `WORK_DIR`. Для дампов
|
||||||
|
запросов заранее создайте `"$WORK_DIR/debug"`; промпты остаются внутри образа.
|
||||||
|
|
||||||
## Установка (Python)
|
## Установка (Python)
|
||||||
|
|
||||||
@@ -28,8 +57,8 @@ cd 2026-linux-sumka
|
|||||||
python3 -m venv .venv
|
python3 -m venv .venv
|
||||||
# Активировать виртуальное окружение
|
# Активировать виртуальное окружение
|
||||||
. .venv/bin/activate
|
. .venv/bin/activate
|
||||||
# Установить зависимости
|
# Установить зависимости под свою видеокарту
|
||||||
pip install -r requirements.txt
|
pip install -r requirements-nvidia.txt # либо requirements-amd.txt
|
||||||
|
|
||||||
# Также должны быть установлены ffmpeg и Chromium/Google Chrome
|
# Также должны быть установлены ffmpeg и Chromium/Google Chrome
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
import traceback
|
import traceback
|
||||||
import shutil
|
|
||||||
import os
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from agent import Agent, AgentMessage
|
from agent import Agent, AgentMessage
|
||||||
from windowizer import Windowizer, Window
|
from windowizer import Windowizer, Window
|
||||||
from asr_filter import AsrFilterResult, AsrFilterSegment
|
from asr_filter import AsrFilterResult, AsrFilterSegment
|
||||||
|
from paths import debug_path, prompt_path
|
||||||
from utils import Timeline, Event
|
from utils import Timeline, Event
|
||||||
|
|
||||||
class _PreEvent(BaseModel):
|
class _PreEvent(BaseModel):
|
||||||
@@ -41,21 +40,19 @@ class AsrEventizer:
|
|||||||
role="user"
|
role="user"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
debug_dir = None
|
debug_dir = debug_path("AsrEventizer", AsrEventizer.DEBUG_ID)
|
||||||
if os.path.isdir("debug"):
|
|
||||||
debug_dir = f"debug/AsrEventizer/{AsrEventizer.DEBUG_ID}"
|
|
||||||
AsrEventizer.DEBUG_ID += 1
|
|
||||||
os.makedirs(debug_dir, exist_ok=True)
|
|
||||||
if debug_dir:
|
if debug_dir:
|
||||||
with open(f"{debug_dir}/request.txt", "w") as f:
|
AsrEventizer.DEBUG_ID += 1
|
||||||
f.write(messages[1].content)
|
(debug_dir / "request.txt").write_text(messages[1].content, encoding="utf-8")
|
||||||
retries_left = 5
|
retries_left = 5
|
||||||
while retries_left > 0:
|
while retries_left > 0:
|
||||||
retries_left -= 1
|
retries_left -= 1
|
||||||
response = self._agent.completion(messages=messages, response_format=_EventizeResult)
|
response = self._agent.completion(messages=messages, response_format=_EventizeResult)
|
||||||
if debug_dir:
|
if debug_dir:
|
||||||
with open(f"{debug_dir}/{retries_left}-retries-left.txt", "w") as f:
|
(debug_dir / f"{retries_left}-retries-left.txt").write_text(
|
||||||
f.write(response)
|
response,
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
# validate data
|
# validate data
|
||||||
try:
|
try:
|
||||||
response = json.loads(response)
|
response = json.loads(response)
|
||||||
@@ -85,10 +82,9 @@ class AsrEventizer:
|
|||||||
"""
|
"""
|
||||||
self._agent = agent
|
self._agent = agent
|
||||||
self._windowizer = windowizer
|
self._windowizer = windowizer
|
||||||
with open("prompts/asr_eventizer.md", "r") as f:
|
|
||||||
self._system_prompt = AgentMessage(
|
self._system_prompt = AgentMessage(
|
||||||
content=f.read(),
|
content=prompt_path("asr_eventizer.md").read_text(encoding="utf-8"),
|
||||||
role="system"
|
role="system",
|
||||||
)
|
)
|
||||||
|
|
||||||
def eventize(self, asr_filter_result: AsrFilterResult) -> Timeline:
|
def eventize(self, asr_filter_result: AsrFilterResult) -> Timeline:
|
||||||
|
|||||||
24
paths.py
Normal file
24
paths.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
"""Paths for bundled resources and per-run artifacts."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
SOURCE_DIR = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_path(name: str) -> Path:
|
||||||
|
"""Return a prompt bundled with the application source code."""
|
||||||
|
path = SOURCE_DIR / "prompts" / name
|
||||||
|
if not path.is_file():
|
||||||
|
raise FileNotFoundError(f"Prompt not found: {path}")
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def debug_path(component: str, index: int) -> Path | None:
|
||||||
|
"""Create a debug directory in the current working directory when enabled."""
|
||||||
|
root = Path.cwd() / "debug"
|
||||||
|
if not root.is_dir():
|
||||||
|
return None
|
||||||
|
path = root / component / str(index)
|
||||||
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
|
return path
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import html
|
import html
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -277,6 +278,8 @@ class PdfBuilder:
|
|||||||
f"--print-to-pdf={destination}",
|
f"--print-to-pdf={destination}",
|
||||||
html_path.as_uri(),
|
html_path.as_uri(),
|
||||||
]
|
]
|
||||||
|
if os.geteuid() == 0:
|
||||||
|
command.insert(1, "--no-sandbox")
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
command,
|
command,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from typing import Literal
|
|||||||
from pydantic import BaseModel, ConfigDict, ValidationError, model_validator
|
from pydantic import BaseModel, ConfigDict, ValidationError, model_validator
|
||||||
|
|
||||||
from agent import Agent, AgentMessage
|
from agent import Agent, AgentMessage
|
||||||
|
from paths import debug_path, prompt_path
|
||||||
from utils import Event, Timeline
|
from utils import Event, Timeline
|
||||||
from video_references import UnresolvedReference, UnresolvedReferences
|
from video_references import UnresolvedReference, UnresolvedReferences
|
||||||
|
|
||||||
@@ -94,10 +95,14 @@ class ReferenceResolver:
|
|||||||
self._image_dir = Path(image_dir)
|
self._image_dir = Path(image_dir)
|
||||||
self._frame_interval = frame_interval
|
self._frame_interval = frame_interval
|
||||||
self._max_frames = max_frames
|
self._max_frames = max_frames
|
||||||
with open("prompts/reference_resolver.md", "r", encoding="utf-8") as f:
|
self._system_prompt = AgentMessage(
|
||||||
self._system_prompt = AgentMessage(content=f.read(), role="system")
|
content=prompt_path("reference_resolver.md").read_text(encoding="utf-8"),
|
||||||
with open("prompts/reference_resolver_ocr.md", "r", encoding="utf-8") as f:
|
role="system",
|
||||||
self._ocr_prompt = AgentMessage(content=f.read(), role="system")
|
)
|
||||||
|
self._ocr_prompt = AgentMessage(
|
||||||
|
content=prompt_path("reference_resolver_ocr.md").read_text(encoding="utf-8"),
|
||||||
|
role="system",
|
||||||
|
)
|
||||||
|
|
||||||
def _sampling_timestamps(self, start: float, end: float) -> list[float]:
|
def _sampling_timestamps(self, start: float, end: float) -> list[float]:
|
||||||
start = max(0.0, start)
|
start = max(0.0, start)
|
||||||
@@ -217,13 +222,10 @@ class ReferenceResolver:
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
debug_dir = None
|
debug_dir = debug_path("ReferenceResolver", ReferenceResolver.DEBUG_ID)
|
||||||
if os.path.isdir("debug"):
|
if debug_dir:
|
||||||
debug_dir = f"debug/ReferenceResolver/{ReferenceResolver.DEBUG_ID}"
|
|
||||||
ReferenceResolver.DEBUG_ID += 1
|
ReferenceResolver.DEBUG_ID += 1
|
||||||
os.makedirs(debug_dir, exist_ok=True)
|
(debug_dir / "request.txt").write_text(messages[1].content, encoding="utf-8")
|
||||||
with open(f"{debug_dir}/request.txt", "w", encoding="utf-8") as f:
|
|
||||||
f.write(messages[1].content)
|
|
||||||
|
|
||||||
retries_left = self.MAX_RETRIES
|
retries_left = self.MAX_RETRIES
|
||||||
while retries_left > 0:
|
while retries_left > 0:
|
||||||
@@ -235,12 +237,10 @@ class ReferenceResolver:
|
|||||||
response_format=_FrameResult,
|
response_format=_FrameResult,
|
||||||
)
|
)
|
||||||
if debug_dir:
|
if debug_dir:
|
||||||
with open(
|
(debug_dir / f"{retries_left}-retries-left.txt").write_text(
|
||||||
f"{debug_dir}/{retries_left}-retries-left.txt",
|
response,
|
||||||
"w",
|
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
) as f:
|
)
|
||||||
f.write(response)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = _FrameResult.model_validate_json(response)
|
result = _FrameResult.model_validate_json(response)
|
||||||
@@ -312,14 +312,11 @@ class ReferenceResolver:
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
debug_dir = None
|
debug_dir = debug_path("ReferenceResolverOCR", ReferenceResolver.OCR_DEBUG_ID)
|
||||||
if os.path.isdir("debug"):
|
if debug_dir:
|
||||||
debug_dir = f"debug/ReferenceResolverOCR/{ReferenceResolver.OCR_DEBUG_ID}"
|
|
||||||
ReferenceResolver.OCR_DEBUG_ID += 1
|
ReferenceResolver.OCR_DEBUG_ID += 1
|
||||||
os.makedirs(debug_dir, exist_ok=True)
|
(debug_dir / "request.txt").write_text(messages[1].content, encoding="utf-8")
|
||||||
with open(f"{debug_dir}/request.txt", "w", encoding="utf-8") as f:
|
shutil.copyfile(crop_path, debug_dir / "crop.jpg")
|
||||||
f.write(messages[1].content)
|
|
||||||
shutil.copyfile(crop_path, f"{debug_dir}/crop.jpg")
|
|
||||||
|
|
||||||
retries_left = self.MAX_RETRIES
|
retries_left = self.MAX_RETRIES
|
||||||
while retries_left > 0:
|
while retries_left > 0:
|
||||||
@@ -331,12 +328,10 @@ class ReferenceResolver:
|
|||||||
response_format=_OCRResult,
|
response_format=_OCRResult,
|
||||||
)
|
)
|
||||||
if debug_dir:
|
if debug_dir:
|
||||||
with open(
|
(debug_dir / f"{retries_left}-retries-left.txt").write_text(
|
||||||
f"{debug_dir}/{retries_left}-retries-left.txt",
|
response,
|
||||||
"w",
|
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
) as f:
|
)
|
||||||
f.write(response)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = _OCRResult.model_validate_json(response)
|
result = _OCRResult.model_validate_json(response)
|
||||||
|
|||||||
3
requirements-amd.txt
Normal file
3
requirements-amd.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-r requirements.txt
|
||||||
|
--extra-index-url https://download.pytorch.org/whl/rocm7.2
|
||||||
|
torch==2.14.0+rocm7.2
|
||||||
3
requirements-nvidia.txt
Normal file
3
requirements-nvidia.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-r requirements.txt
|
||||||
|
--extra-index-url https://download.pytorch.org/whl/cu130
|
||||||
|
torch==2.14.0+cu130
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
httpx2
|
httpx2==2.13.0
|
||||||
latex2mathml
|
latex2mathml==3.81.1
|
||||||
openai
|
openai==3.14.0
|
||||||
openai-whisper
|
openai-whisper==20250625
|
||||||
pydantic>=2
|
pydantic==2.13.5
|
||||||
torch
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Annotated, Literal
|
from typing import Annotated, Literal
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator
|
from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator
|
||||||
|
|
||||||
from agent import Agent, AgentMessage
|
from agent import Agent, AgentMessage
|
||||||
|
from paths import debug_path, prompt_path
|
||||||
from utils import Event, Timeline
|
from utils import Event, Timeline
|
||||||
from windowizer import Window, Windowizer
|
from windowizer import Window, Windowizer
|
||||||
|
|
||||||
@@ -146,8 +146,10 @@ class StructureBuilder:
|
|||||||
"""
|
"""
|
||||||
self._agent = agent
|
self._agent = agent
|
||||||
self._windowizer = windowizer
|
self._windowizer = windowizer
|
||||||
with open("prompts/structure_builder.md", "r", encoding="utf-8") as f:
|
self._system_prompt = AgentMessage(
|
||||||
self._system_prompt = AgentMessage(content=f.read(), role="system")
|
content=prompt_path("structure_builder.md").read_text(encoding="utf-8"),
|
||||||
|
role="system",
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_result(result: _BuildResult, window: Window[Event]) -> None:
|
def _validate_result(result: _BuildResult, window: Window[Event]) -> None:
|
||||||
@@ -192,25 +194,20 @@ class StructureBuilder:
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
debug_dir = None
|
debug_dir = debug_path("StructureBuilder", StructureBuilder.DEBUG_ID)
|
||||||
if os.path.isdir("debug"):
|
if debug_dir:
|
||||||
debug_dir = f"debug/StructureBuilder/{StructureBuilder.DEBUG_ID}"
|
|
||||||
StructureBuilder.DEBUG_ID += 1
|
StructureBuilder.DEBUG_ID += 1
|
||||||
os.makedirs(debug_dir, exist_ok=True)
|
(debug_dir / "request.txt").write_text(messages[1].content, encoding="utf-8")
|
||||||
with open(f"{debug_dir}/request.txt", "w", encoding="utf-8") as f:
|
|
||||||
f.write(messages[1].content)
|
|
||||||
|
|
||||||
retries_left = self.MAX_RETRIES
|
retries_left = self.MAX_RETRIES
|
||||||
while retries_left > 0:
|
while retries_left > 0:
|
||||||
retries_left -= 1
|
retries_left -= 1
|
||||||
response = self._agent.completion(messages=messages, response_format=_BuildResult)
|
response = self._agent.completion(messages=messages, response_format=_BuildResult)
|
||||||
if debug_dir:
|
if debug_dir:
|
||||||
with open(
|
(debug_dir / f"{retries_left}-retries-left.txt").write_text(
|
||||||
f"{debug_dir}/{retries_left}-retries-left.txt",
|
response,
|
||||||
"w",
|
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
) as f:
|
)
|
||||||
f.write(response)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = _BuildResult.model_validate_json(response)
|
result = _BuildResult.model_validate_json(response)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator
|
from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator
|
||||||
|
|
||||||
from agent import Agent, AgentMessage
|
from agent import Agent, AgentMessage
|
||||||
|
from paths import debug_path, prompt_path
|
||||||
from structure_builder import ImageElement, Structure, StructureElement
|
from structure_builder import ImageElement, Structure, StructureElement
|
||||||
from windowizer import Window, Windowizer
|
from windowizer import Window, Windowizer
|
||||||
|
|
||||||
@@ -39,8 +39,10 @@ class StructureRefiner:
|
|||||||
def __init__(self, agent: Agent, windowizer: Windowizer[StructureElement]) -> None:
|
def __init__(self, agent: Agent, windowizer: Windowizer[StructureElement]) -> None:
|
||||||
self._agent = agent
|
self._agent = agent
|
||||||
self._windowizer = windowizer
|
self._windowizer = windowizer
|
||||||
with open("prompts/structure_refiner.md", "r", encoding="utf-8") as f:
|
self._system_prompt = AgentMessage(
|
||||||
self._system_prompt = AgentMessage(content=f.read(), role="system")
|
content=prompt_path("structure_refiner.md").read_text(encoding="utf-8"),
|
||||||
|
role="system",
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_result(
|
def _validate_result(
|
||||||
@@ -69,13 +71,10 @@ class StructureRefiner:
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
debug_dir = None
|
debug_dir = debug_path("StructureRefiner", StructureRefiner.DEBUG_ID)
|
||||||
if os.path.isdir("debug"):
|
if debug_dir:
|
||||||
debug_dir = f"debug/StructureRefiner/{StructureRefiner.DEBUG_ID}"
|
|
||||||
StructureRefiner.DEBUG_ID += 1
|
StructureRefiner.DEBUG_ID += 1
|
||||||
os.makedirs(debug_dir, exist_ok=True)
|
(debug_dir / "request.txt").write_text(messages[1].content, encoding="utf-8")
|
||||||
with open(f"{debug_dir}/request.txt", "w", encoding="utf-8") as f:
|
|
||||||
f.write(messages[1].content)
|
|
||||||
|
|
||||||
retries_left = self.MAX_RETRIES
|
retries_left = self.MAX_RETRIES
|
||||||
while retries_left > 0:
|
while retries_left > 0:
|
||||||
@@ -85,12 +84,10 @@ class StructureRefiner:
|
|||||||
response_format=_RefineResult,
|
response_format=_RefineResult,
|
||||||
)
|
)
|
||||||
if debug_dir:
|
if debug_dir:
|
||||||
with open(
|
(debug_dir / f"{retries_left}-retries-left.txt").write_text(
|
||||||
f"{debug_dir}/{retries_left}-retries-left.txt",
|
response,
|
||||||
"w",
|
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
) as f:
|
)
|
||||||
f.write(response)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = _RefineResult.model_validate_json(response)
|
result = _RefineResult.model_validate_json(response)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator
|
from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator
|
||||||
|
|
||||||
from agent import Agent, AgentMessage
|
from agent import Agent, AgentMessage
|
||||||
|
from paths import debug_path, prompt_path
|
||||||
from utils import Timeline
|
from utils import Timeline
|
||||||
from windowizer import Window, Windowizer
|
from windowizer import Window, Windowizer
|
||||||
|
|
||||||
@@ -71,8 +71,10 @@ class VideoReferenceBuilder:
|
|||||||
) -> None:
|
) -> None:
|
||||||
self._agent = agent
|
self._agent = agent
|
||||||
self._windowizer = windowizer
|
self._windowizer = windowizer
|
||||||
with open("prompts/video_references.md", "r", encoding="utf-8") as f:
|
self._system_prompt = AgentMessage(
|
||||||
self._system_prompt = AgentMessage(content=f.read(), role="system")
|
content=prompt_path("video_references.md").read_text(encoding="utf-8"),
|
||||||
|
role="system",
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_result(
|
def _validate_result(
|
||||||
@@ -113,13 +115,10 @@ class VideoReferenceBuilder:
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
debug_dir = None
|
debug_dir = debug_path("VideoReferenceBuilder", self.DEBUG_ID)
|
||||||
if os.path.isdir("debug"):
|
if debug_dir:
|
||||||
debug_dir = f"debug/VideoReferenceBuilder/{self.DEBUG_ID}"
|
|
||||||
VideoReferenceBuilder.DEBUG_ID += 1
|
VideoReferenceBuilder.DEBUG_ID += 1
|
||||||
os.makedirs(debug_dir, exist_ok=True)
|
(debug_dir / "request.txt").write_text(messages[1].content, encoding="utf-8")
|
||||||
with open(f"{debug_dir}/request.txt", "w", encoding="utf-8") as f:
|
|
||||||
f.write(messages[1].content)
|
|
||||||
|
|
||||||
retries_left = self.MAX_RETRIES
|
retries_left = self.MAX_RETRIES
|
||||||
while retries_left > 0:
|
while retries_left > 0:
|
||||||
@@ -129,12 +128,10 @@ class VideoReferenceBuilder:
|
|||||||
response_format=_WindowResult,
|
response_format=_WindowResult,
|
||||||
)
|
)
|
||||||
if debug_dir:
|
if debug_dir:
|
||||||
with open(
|
(debug_dir / f"{retries_left}-retries-left.txt").write_text(
|
||||||
f"{debug_dir}/{retries_left}-retries-left.txt",
|
response,
|
||||||
"w",
|
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
) as f:
|
)
|
||||||
f.write(response)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = _WindowResult.model_validate_json(response)
|
result = _WindowResult.model_validate_json(response)
|
||||||
|
|||||||
Reference in New Issue
Block a user