generated from john/python-template
claude-sonnet-5 review: Phase 5 (final) implemented by gpt-5.3-codex
Quality Gate / gate (push) Failing after 37s
Quality Gate / gate (push) Failing after 37s
This commit is contained in:
@@ -32,6 +32,8 @@ DEFAULT_PROMPT_NAME=transcribe_document.md
|
|||||||
# --- runtime environment ---
|
# --- runtime environment ---
|
||||||
# ENVIRONMENT: development | test | production
|
# ENVIRONMENT: development | test | production
|
||||||
ENVIRONMENT=development
|
ENVIRONMENT=development
|
||||||
|
# TRANSCRIPTION_COMMIT default: unset (optional build/commit identifier for provenance evidence)
|
||||||
|
# TRANSCRIPTION_COMMIT=
|
||||||
|
|
||||||
# --- persistence ---
|
# --- persistence ---
|
||||||
# Use nested keys (env_nested_delimiter="__").
|
# Use nested keys (env_nested_delimiter="__").
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
"""Health endpoint routes."""
|
"""Health endpoint routes."""
|
||||||
|
|
||||||
|
from typing import NotRequired
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
|
|
||||||
@@ -8,10 +11,21 @@ from transcription.worker import resolve_worker_health
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
def healthz(request: Request) -> dict[str, object]:
|
class WorkerHealthPayload(TypedDict):
|
||||||
|
state: str
|
||||||
|
error_id: NotRequired[str]
|
||||||
|
error_category: NotRequired[str]
|
||||||
|
|
||||||
|
|
||||||
|
class HealthPayload(TypedDict):
|
||||||
|
status: str
|
||||||
|
worker: WorkerHealthPayload
|
||||||
|
|
||||||
|
|
||||||
|
def healthz(request: Request) -> HealthPayload:
|
||||||
"""Return health status with worker-liveness signal."""
|
"""Return health status with worker-liveness signal."""
|
||||||
worker = resolve_worker_health(request.app.state)
|
worker = resolve_worker_health(request.app.state)
|
||||||
payload: dict[str, object] = {
|
payload: HealthPayload = {
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"worker": {
|
"worker": {
|
||||||
"state": worker.state,
|
"state": worker.state,
|
||||||
@@ -25,6 +39,6 @@ def healthz(request: Request) -> dict[str, object]:
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/healthz")
|
@router.get("/healthz")
|
||||||
def healthz_route(request: Request) -> dict[str, object]:
|
def healthz_route(request: Request) -> HealthPayload:
|
||||||
"""Route wrapper for health status payload."""
|
"""Route wrapper for health status payload."""
|
||||||
return healthz(request)
|
return healthz(request)
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ class Settings(BaseSettings):
|
|||||||
|
|
||||||
# --- runtime environment ---
|
# --- runtime environment ---
|
||||||
environment: Literal["development", "test", "production"] = "development"
|
environment: Literal["development", "test", "production"] = "development"
|
||||||
|
transcription_commit: NonEmptyStr | None = None
|
||||||
|
|
||||||
# --- persistence ---
|
# --- persistence ---
|
||||||
database: DatabaseSettings = Field(default_factory=SqliteSettings)
|
database: DatabaseSettings = Field(default_factory=SqliteSettings)
|
||||||
@@ -203,7 +204,7 @@ LOGGING_CONFIG: dict[str, Any] = {
|
|||||||
"file": {
|
"file": {
|
||||||
"class": "logging.handlers.RotatingFileHandler",
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
"formatter": "standard",
|
"formatter": "standard",
|
||||||
"filename": str((Path("./data/logs") / "transcription.log")),
|
"filename": str(Path("./data/logs") / "transcription.log"),
|
||||||
"maxBytes": 10 * 1024 * 1024,
|
"maxBytes": 10 * 1024 * 1024,
|
||||||
"backupCount": 5,
|
"backupCount": 5,
|
||||||
"encoding": "utf-8",
|
"encoding": "utf-8",
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import platform
|
import platform
|
||||||
from importlib.metadata import PackageNotFoundError
|
from importlib.metadata import PackageNotFoundError
|
||||||
from importlib.metadata import version
|
from importlib.metadata import version
|
||||||
@@ -17,6 +16,8 @@ from pydantic import ConfigDict
|
|||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
from pydantic import JsonValue
|
from pydantic import JsonValue
|
||||||
|
|
||||||
|
from transcription.config import Settings
|
||||||
|
|
||||||
REQUEST_MANIFEST_SCHEMA = "transcription.request-manifest"
|
REQUEST_MANIFEST_SCHEMA = "transcription.request-manifest"
|
||||||
REQUEST_MANIFEST_VERSION = "1"
|
REQUEST_MANIFEST_VERSION = "1"
|
||||||
SOFTWARE_CONTEXT_SCHEMA = "transcription.software-context"
|
SOFTWARE_CONTEXT_SCHEMA = "transcription.software-context"
|
||||||
@@ -141,11 +142,17 @@ def package_version(package: str) -> str:
|
|||||||
return "unknown"
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
def build_software_context(*, adapter_name: str, adapter_version: str, client_library: str) -> SoftwareContext:
|
def build_software_context(
|
||||||
|
*,
|
||||||
|
adapter_name: str,
|
||||||
|
adapter_version: str,
|
||||||
|
client_library: str,
|
||||||
|
settings: Settings,
|
||||||
|
) -> SoftwareContext:
|
||||||
"""Build the runtime software identity for an execution."""
|
"""Build the runtime software identity for an execution."""
|
||||||
return SoftwareContext(
|
return SoftwareContext(
|
||||||
application_version=package_version("transcription"),
|
application_version=package_version("transcription"),
|
||||||
application_commit=os.environ.get("TRANSCRIPTION_COMMIT") or None,
|
application_commit=settings.transcription_commit,
|
||||||
adapter_name=adapter_name,
|
adapter_name=adapter_name,
|
||||||
adapter_version=adapter_version,
|
adapter_version=adapter_version,
|
||||||
client_library=client_library,
|
client_library=client_library,
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ class OpenRouterTranscriptionProvider:
|
|||||||
adapter_name="openrouter",
|
adapter_name="openrouter",
|
||||||
adapter_version=OPENROUTER_ADAPTER_VERSION,
|
adapter_version=OPENROUTER_ADAPTER_VERSION,
|
||||||
client_library="openrouter",
|
client_library="openrouter",
|
||||||
|
settings=self._settings,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ provider: openrouter
|
|||||||
model: openai/gpt-5.3-codex
|
model: openai/gpt-5.3-codex
|
||||||
---
|
---
|
||||||
[document body typewritten]
|
[document body typewritten]
|
||||||
|
|
||||||
BY WAY OF INTRODUCTION:-
|
BY WAY OF INTRODUCTION:-
|
||||||
|
|
||||||
These few paragraphs of introduction may help you read BOOK 2 which covers a wider range than did BOOK 1 (Pioneer Days).
|
These few paragraphs of introduction may help you read BOOK 2 which covers a wider range than did BOOK 1 (Pioneer Days).
|
||||||
@@ -14,4 +15,5 @@ We promised in BOOK 1 that in BOOK 2 we would give the story of the trip of John
|
|||||||
Some who get this book will consider the group picture the best thing in the book. It took a lot of preliminary photographing to reduce some pictures, enlarge others and bring out the tin types. We wish that instead of 44 faces we could have given 88. Do not blame Ethel Cochran-Shinn for the selection. She furnished enough pictures but we had to take only part of them. We think there are great possibilities in reproducing old pictures. We wish we had a Pickard group. Some Pickard descendant may wish to make a collection.
|
Some who get this book will consider the group picture the best thing in the book. It took a lot of preliminary photographing to reduce some pictures, enlarge others and bring out the tin types. We wish that instead of 44 faces we could have given 88. Do not blame Ethel Cochran-Shinn for the selection. She furnished enough pictures but we had to take only part of them. We think there are great possibilities in reproducing old pictures. We wish we had a Pickard group. Some Pickard descendant may wish to make a collection.
|
||||||
|
|
||||||
We are much impressed with the future possibilities of getting a complete geneology [sic] of the Pickard family. Mr. Cochran has a fine chapter on the Pickards but to date we have not had the pleasure of finding all of the family dates. We had intended to give more family data in this book but it takes time to get the correct dates. Often times it requires trips to cemeteries to get dates on the tombstones. Winter is no time to collect dates on tombstones.
|
We are much impressed with the future possibilities of getting a complete geneology [sic] of the Pickard family. Mr. Cochran has a fine chapter on the Pickards but to date we have not had the pleasure of finding all of the family dates. We had intended to give more family data in this book but it takes time to get the correct dates. Often times it requires trips to cemeteries to get dates on the tombstones. Winter is no time to collect dates on tombstones.
|
||||||
|
|
||||||
~2~
|
~2~
|
||||||
|
|||||||
@@ -5,105 +5,35 @@ model: openai/gpt-5.3-codex
|
|||||||
[document body typeset]
|
[document body typeset]
|
||||||
Family Only
|
Family Only
|
||||||
Home | Sibling's Stories | 1st Cousins | Ancestor's Stories | Reunion History | Next Reunion | JECMEF
|
Home | Sibling's Stories | 1st Cousins | Ancestor's Stories | Reunion History | Next Reunion | JECMEF
|
||||||
|
Family Only
|
||||||
|
|
||||||
OMIE WRITES HOME
|
OMIE WRITES HOME
|
||||||
|
|
||||||
Ed. Note: The following letter was written by Omie Cochran in Nome, Alaska and sent to her
|
Ed. Note: The following letter was written by Omie Cochran in Nome, Alaska and sent to her sister, Ethel Shinn, in Canfield, Idaho in 1923. It has been stored away these 63 years in the original envelope with its 2 cent stamp. The letter has a number of references to the Shinn children. Peter was Louis; Polly was Edith; and the 'little black rascal' referred to Maurice. Miss Saville was the nurse at the Nome Hospital that was mentioned in the article by Inez in the family newsletter two years ago.
|
||||||
sister, Ethel Shinn, in Canfield, Idaho in 1923. It has been stored away these 63 years in the
|
|
||||||
original envelope with its 2 cent stamp. The letter has a number of references to the Shinn
|
|
||||||
children. Peter was Louis; Polly was Edith; and the 'little black rascal' referred to Maurice.
|
|
||||||
Miss Saville was the nurse at the Nome Hospital that was mentioned in the article by Inez in
|
|
||||||
the family newsletter two years ago.
|
|
||||||
|
|
||||||
Nome Alaska August 26, 1923
|
Nome Alaska August 26, 1923
|
||||||
|
|
||||||
My Dear Ethel et al.
|
My Dear Ethel et al.
|
||||||
|
|
||||||
I don't know when I did write or when you did
|
I don't know when I did write or when you did but I am going to write now however and never the less. But I wish I could talk (I can yet but I mean to tell you all) instead and see ole Unc Pete and Polly sit up and listen and that little black rascal of yours would fairly sparkle with listening. Can't I see him listening now to all the yarns we told last summer?
|
||||||
but I am going to write now however and never
|
|
||||||
the less. But I wish I could talk (I can yet but I
|
|
||||||
mean to tell you all) instead and see ole Unc Pete
|
|
||||||
and Polly sit up and listen and that little black
|
|
||||||
rascal of yours would fairly sparkle with
|
|
||||||
listening. Can't I see him listening now to all the
|
|
||||||
yarns we told last summer?
|
|
||||||
|
|
||||||
You see, we-Miss Saville and I, took a trip north
|
You see, we-Miss Saville and I, took a trip north on the Buford and it was very interesting. We went north thru the Bering Strait into the Arctic and as far as the Ice Pack. There the captain of our craft and some other mighty hunters went out first in kayaks and later in row boats and shot seven walrus. When they also took a movie man and camera, so you will likely see all this in the movies before I get to tell you. They came back on board and the ship went up along the icebergs and the walrus were 'heisted' on board by the use of the crane which loads tons of freight and the beasts were so huge that they made the pulleys just creak. They were over 12 feet long, as big around as three cows, had no feet but toenails on their flippers or flappers, no head but their body just suddenly ended with a hole for a mouth and big bristles all around it similar to a currycomb in coarseness; no ears but huge tusks of ivory. They are the most repulsive looking animals imaginable and tho I have always read about them I never expect such disagreeable looking creatures. They had a rough brown hairy skin and some of them looked warty. They must have weighed two ton at least. Ere we got them back to Nome to the natives they were getting extremely odiferous-in fact, you could scarcely stay on the ship with any degree of comfort unless you had per chance lost your sense of smell.
|
||||||
on the Buford and it was very interesting. We
|
|
||||||
went north thru the Bering Strait into the Arctic and as far as the Ice Pack. There the captain
|
|
||||||
of our craft and some other mighty hunters went out first in kayaks and later in row boats and
|
|
||||||
shot seven walrus. When they also took a movie man and camera, so you will likely see all
|
|
||||||
this in the movies before I get to tell you. They came back on board and the ship went up
|
|
||||||
along the icebergs and the walrus were 'heisted' on board by the use of the crane which loads
|
|
||||||
tons of freight and the beasts were so huge that they made the pulleys just creak. They were
|
|
||||||
over 12 feet long, as big around as three cows, had no feet but toenails on their flippers or
|
|
||||||
flappers, no head but their body just suddenly ended with a hole for a mouth and big bristles
|
|
||||||
|
|
||||||
[photograph: black-and-white photo of children standing on snow]
|
Then we went north to a few minutes beyond the 70th degree of latitude and thot [sic] for awhile we would go to Wrangell Island where some men from Steffonsons ship were supposed to be stranded but we didn't get there and instead stopped at a small native village at Cape Serdz in Siberia. These Eskimo were very primitive. One white squaw man lived there and had for 23 years. He was a Swede--who else could. Their houses were circular and built up with dirt 2 or 3 feet and then skins were stretched over it and weighted down with rocks. Inside, the room was partitioned off at the sides with skins for sleeping quarters. In the main part they had the fire on the ground and the fish drying on lines and the skins hanging around and the dogs and babies and children. They wore skin clothes entirely. The women's were made like bloomers and were heavily padded for warmth. They wore high mukluks and really looked very comfortable. The babies were in fur skins with the fur inside and they looked like little Teddy bears with faces. I guess they had never seen white women, not so many at one time anyway. We went ashore in 2 life boats and a launch pulling them. On the launch was a six piece band playing and the rear of the last life boat was the movie man. 'Twas very thrilling.
|
||||||
|
|
||||||
all around it similar to a currycomb in coarseness; no ears but huge tusks of ivory. They are
|
The other place we stopped was at Whalen, a trading post in Siberia. There these 'towerists' went wild. They rushed helter-skelter, hither and thither, here and there, trying to find something to buy. Prices raised right before your eyes. One would but [sic] something for $1.00 and the next might have to pay $2.00, $4.00 or $10.00. That made no difference. They had to have it. One man I was sort of taking care of, tho he had his son along for the purpose, bought 2 ivory tusks, 1 pup, 2 moccasins, 3 or 4 billikens, 6 or 8 ivory and silver rings, one fishing line, hooks, floats, etc. and two bird slings. The slings have rocks at the end and the little natives throw them at the flocks of geese and ducks which fly close over the village and the slings entangle their wings and legs, sometimes more than one, and they can't fly. They come down and the natives capture them. There was more junk brot aboard than baggage, I do believe. And they say that at the first stop it was worse than here. The red flag was flying over Whalen and the Russian soldiers were there-a few, one or two or three, I forget the number.
|
||||||
the most repulsive looking animals imaginable and tho I have always read about them I never
|
|
||||||
expect such disagreeable looking creatures. They had a rough brown hairy skin and some of
|
|
||||||
them looked warty. They must have weighed two ton at least. Ere we got them back to Nome
|
|
||||||
to the natives they were getting extremely odiferous—in fact, you could scarcely stay on the
|
|
||||||
ship with any degree of comfort unless you had per chance lost your sense of smell.
|
|
||||||
|
|
||||||
Then we went north to a few minutes beyond the 70th degree of latitude and thot [sic] for awhile
|
We got home yesterday morning at 5 a.m. but missed the first lighter in so had to stay out until 2:30. The girls had prepared a big meal for us and invited up the Hartfords and then let us talk. Miss Saville talked quite a bit. Any how if you folks don't like this I don't care, it is all I had to write about and I know Buster'ud listen anyway and I'd soak ole Peter's head if he didn't and Polly would in my lap and I don't know much about the youngest one of yours so likely he would be squawling. But we did surely enjoy our trip and were gone just long enuf.
|
||||||
we would go to Wrangell Island where some men from Steffonsons ship were supposed to be
|
|
||||||
stranded but we didn't get there and instead stopped at a small native village at Cape Serdz in
|
|
||||||
Siberia. These Eskimo were very primitive. One white squaw man lived there and had for 23
|
|
||||||
years. He was a Swede--who else could. Their houses were circular and built up with dirt 2 or
|
|
||||||
3 feet and then skins were stretched over it and weighted down with rocks. Inside, the room
|
|
||||||
was partitioned off at the sides with skins for sleeping quarters. In the main part they had the
|
|
||||||
fire on the ground and the fish drying on lines and the skins hanging around and the dogs and
|
|
||||||
babies and children. They wore skin clothes entirely. The women's were made like bloomers
|
|
||||||
and were heavily padded for warmth. They wore high mukluks and really looked very
|
|
||||||
comfortable. The babies were in fur skins with the fur inside and they looked like little Teddy
|
|
||||||
bears with faces. I guess they had never seen white women, not so many at one time anyway.
|
|
||||||
We went ashore in 2 life boats and a launch pulling them. On the launch was a six piece band
|
|
||||||
playing and the rear of the last life boat was the movie man. 'Twas very thrilling.
|
|
||||||
|
|
||||||
The other place we stopped was at Whalen, a trading post in Siberia. There these 'towerists'
|
[photograph of a group of people standing outdoors in snow]
|
||||||
went wild. They rushed helter-skelter, hither and thither, here and there, trying to find
|
|
||||||
something to buy. Prices raised right before your eyes. One would buy something for $1.00
|
|
||||||
and the next might have to pay $2.00, $4.00 or $10.00. That made no difference. They had to
|
|
||||||
have it. One man I was sort of taking care of, tho he had his son along for the purpose,
|
|
||||||
bought 2 ivory tusks, 1 pup, 2 moccasins, 3 or 4 billikens, 6 or 8 ivory and silver rings, one
|
|
||||||
fishing line, hooks, floats, etc. and two bird slings. The slings have rocks at the end and the
|
|
||||||
little natives throw them at the flocks of geese and ducks which fly close over the village and
|
|
||||||
the slings entangle their wings and legs, sometimes more than one, and they can't fly. They
|
|
||||||
come down and the natives capture them. There was more junk brot [sic] aboard than baggage, I
|
|
||||||
do believe. And they say that at the first stop it was worse than here. The red flag was flying
|
|
||||||
over Whalen and the Russian soldiers were there—a few, one or two or three, I forget the
|
|
||||||
number.
|
|
||||||
|
|
||||||
We got home yesterday morning at 5 a.m. but missed the first lighter in so had to stay out
|
I expect there were 150 passengers on board and almost or more of the crew and helpers. We had a stateroom down next to the kitchen and 'twas pretty fierce for odor at times.
|
||||||
until 2:30. The girls had prepared a big meal for us and invited up the Hartfords and then let
|
|
||||||
us talk. Miss Saville talked quite a bit. Any how if you folks don't like this I don't care, it is
|
|
||||||
all I had to write about and I know Buster'ud listen anyway and I'd soak ole Peter's head if he
|
|
||||||
didn't and Polly would in my lap and I don't know much about the youngest one of yours so
|
|
||||||
likely he would be squawling. But we did surely enjoy our trip and were gone just long enuf [sic].
|
|
||||||
|
|
||||||
I expect there were 150 passengers on board and almost or more of the crew and helpers. We
|
I have had jobs nearly all summer but not very much in them. Next week, September 4, school opens. I wish they would wait for a week but you know these school men. Wouldn't make any special difference I suppose for I would just fritter away the time but still one likes to postpone the inevitable.
|
||||||
had a stateroom down next to the kitchen and 'twas pretty fierce for odor at times.
|
|
||||||
|
|
||||||
I have had jobs nearly all summer but not very much in them. Next week, September 4,
|
I just now stopped and re-read your letter and it sure was a bird. I don't especially blame Ray for reading over your shoulder. It would seem, then that you have bright children. Maybe they do know something about Geography. But it is ridiculous to speak of Louis finishing the eighth grade. Why you and I were grown children when we finished and he is only a baby. I am rather afraid he doesn't know much. I quite remember your little timid Maurice and how he shifted his affection from his Aunt Om and yelled and howled and screamed steadily for a week while his mother went to S.S. Ask him is he recalls this little interview. Do you suppose he does?
|
||||||
school opens. I wish they would wait for a week but you know these school men. Wouldn't
|
|
||||||
make any special difference I suppose for I would just fritter away the time but still one likes
|
|
||||||
to postpone the inevitable.
|
|
||||||
|
|
||||||
I just now stopped and re-read your letter and it sure was a bird. I don't especially blame Ray
|
Ever hear from Zen? or Inez? They don't seem to be very writing inclined tho Zen has done well this year. Even sent me a telegram a few weeks ago. Well, if anything else ever happens, I'll write again. Don't suppose it ever will, tho.
|
||||||
for reading over your shoulder. It would seem, then that you have bright children. Maybe
|
|
||||||
they do know something about Geography. But it is ridiculous to speak of Louis finishing the
|
|
||||||
eighth grade. Why you and I were grown children when we finished and he is only a baby. I
|
|
||||||
am rather afraid he doesn't know much. I quite remember your little timid Maurice and how
|
|
||||||
he shifted his affection from his Aunt Om and yelled and howled and screamed steadily for a
|
|
||||||
week while his mother went to S.S. Ask him is he recalls this little interview. Do you suppose
|
|
||||||
he does?
|
|
||||||
|
|
||||||
Ever hear from Zen? or Inez? They don't seem to be very writing inclined tho Zen has done
|
|
||||||
well this year. Even sent me a telegram a few weeks ago. Well, if anything else ever happens,
|
|
||||||
I'll write again. Don't suppose it ever will, tho.
|
|
||||||
|
|
||||||
Lots of love to all,
|
Lots of love to all,
|
||||||
|
|
||||||
|
|||||||
@@ -9,25 +9,25 @@ ISBILL & MOSER
|
|||||||
DEALERS IN
|
DEALERS IN
|
||||||
GENERAL MERCHANDISE
|
GENERAL MERCHANDISE
|
||||||
|
|
||||||
Vonore, Tenn., [handwritten: Jan'y 27-] 191[handwritten: 3]
|
Vonore, Tenn., [handwritten: July 27-] 191[handwritten: 3]
|
||||||
|
|
||||||
[handwritten: Dear Uncle Aunt & Cousins
|
[handwritten: Dear Uncle Aunt & Cousins
|
||||||
I was at home a
|
I was at home a
|
||||||
few nights ago & saw a
|
few nights ago & saw a
|
||||||
letter from your folks, so
|
letter from your folks, so
|
||||||
I decided to write you
|
I decided to write you
|
||||||
a few lines myself &
|
a few lines myself as
|
||||||
I am [contemplating?] a
|
I am contemplat[ing?] a
|
||||||
trip out west next summer
|
trip out west next summer
|
||||||
& [wyant?] [lot?] of [olders?] to go
|
& want [lots?] to go
|
||||||
where I am from.
|
where I am [stopen?].
|
||||||
|
|
||||||
Am getting
|
Am getting
|
||||||
up in years & [wondering?].
|
up in years & [unmarried?].
|
||||||
So [you?] see the object of
|
so you see the object of
|
||||||
my trip, is to get a wife
|
my trip, is to get a wife
|
||||||
If there is any old maids
|
If there is any old maids
|
||||||
or widows out there I
|
or widows out there, I
|
||||||
want you to [hire?] them
|
want you to [hire?] them
|
||||||
at one [illegible] [illegible] at them
|
at [par?] and [marry?] me [to?] them
|
||||||
as soon as I get there.]
|
as soon as I get there.]
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from sqlalchemy.ext.asyncio import async_sessionmaker
|
||||||
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
from transcription.config import Settings
|
from transcription.config import Settings
|
||||||
from transcription.services.base import ServiceBase
|
from transcription.services.base import ServiceBase
|
||||||
@@ -28,14 +31,22 @@ def _settings() -> Settings:
|
|||||||
return Settings(_env_file=None, openrouter_api_key="test-key")
|
return Settings(_env_file=None, openrouter_api_key="test-key")
|
||||||
|
|
||||||
|
|
||||||
|
def _session_factory_stub() -> async_sessionmaker[AsyncSession]:
|
||||||
|
return cast(async_sessionmaker[AsyncSession], object())
|
||||||
|
|
||||||
|
|
||||||
|
def _session_stub() -> AsyncSession:
|
||||||
|
return cast(AsyncSession, object())
|
||||||
|
|
||||||
|
|
||||||
def test_initializes_with_defaults(monkeypatch):
|
def test_initializes_with_defaults(monkeypatch):
|
||||||
settings = _settings()
|
settings = _settings()
|
||||||
session_factory = object()
|
session_factory = _session_factory_stub()
|
||||||
|
|
||||||
monkeypatch.setattr("transcription.services.base.get_settings", lambda: settings)
|
monkeypatch.setattr("transcription.services.base.get_settings", lambda: settings)
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"transcription.services.base.resolve_session_factory",
|
"transcription.services.base.resolve_session_factory",
|
||||||
lambda **kwargs: session_factory,
|
lambda **_kwargs: session_factory,
|
||||||
)
|
)
|
||||||
|
|
||||||
service = ServiceBase()
|
service = ServiceBase()
|
||||||
@@ -46,7 +57,7 @@ def test_initializes_with_defaults(monkeypatch):
|
|||||||
|
|
||||||
def test_initializes_with_custom_session_factory():
|
def test_initializes_with_custom_session_factory():
|
||||||
settings = _settings()
|
settings = _settings()
|
||||||
session_factory = object()
|
session_factory = _session_factory_stub()
|
||||||
|
|
||||||
service = ServiceBase(settings=settings, session_factory=session_factory)
|
service = ServiceBase(settings=settings, session_factory=session_factory)
|
||||||
|
|
||||||
@@ -57,9 +68,9 @@ def test_initializes_with_custom_session_factory():
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_session_scope_reuses_provided_session(monkeypatch):
|
async def test_session_scope_reuses_provided_session(monkeypatch):
|
||||||
captured: dict[str, object | None] = {}
|
captured: dict[str, object | None] = {}
|
||||||
provided_session = object()
|
provided_session = _session_stub()
|
||||||
yielded = object()
|
yielded = _session_stub()
|
||||||
service = ServiceBase(settings=_settings(), session_factory=object())
|
service = ServiceBase(settings=_settings(), session_factory=_session_factory_stub())
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def _fake_session_scope(*, session_factory=None, session=None):
|
async def _fake_session_scope(*, session_factory=None, session=None):
|
||||||
@@ -78,8 +89,8 @@ async def test_session_scope_reuses_provided_session(monkeypatch):
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_session_scope_creates_owned_session_when_none_provided(monkeypatch):
|
async def test_session_scope_creates_owned_session_when_none_provided(monkeypatch):
|
||||||
captured: dict[str, object | None] = {}
|
captured: dict[str, object | None] = {}
|
||||||
owned_session = object()
|
owned_session = _session_stub()
|
||||||
service = ServiceBase(settings=_settings(), session_factory=object())
|
service = ServiceBase(settings=_settings(), session_factory=_session_factory_stub())
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def _fake_session_scope(*, session_factory=None, session=None):
|
async def _fake_session_scope(*, session_factory=None, session=None):
|
||||||
@@ -97,12 +108,12 @@ async def test_session_scope_creates_owned_session_when_none_provided(monkeypatc
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_session_scope_propagates_exceptions(monkeypatch):
|
async def test_session_scope_propagates_exceptions(monkeypatch):
|
||||||
service = ServiceBase(settings=_settings(), session_factory=object())
|
service = ServiceBase(settings=_settings(), session_factory=_session_factory_stub())
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def _fake_session_scope(*, session_factory=None, session=None):
|
async def _fake_session_scope(*, session_factory=None, session=None):
|
||||||
_ = (session_factory, session)
|
_ = (session_factory, session)
|
||||||
yield object()
|
yield _session_stub()
|
||||||
|
|
||||||
monkeypatch.setattr("transcription.services.base.session_scope", _fake_session_scope)
|
monkeypatch.setattr("transcription.services.base.session_scope", _fake_session_scope)
|
||||||
|
|
||||||
@@ -113,11 +124,15 @@ async def test_session_scope_propagates_exceptions(monkeypatch):
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_finalize_commits_for_service_owned_session():
|
async def test_finalize_commits_for_service_owned_session():
|
||||||
service = ServiceBase(settings=_settings(), session_factory=object())
|
service = ServiceBase(settings=_settings(), session_factory=_session_factory_stub())
|
||||||
session = _TrackingSession()
|
session = _TrackingSession()
|
||||||
refreshed = object()
|
refreshed = object()
|
||||||
|
|
||||||
await service._finalize(session=session, caller_session=None, refresh=(refreshed,))
|
await service._finalize(
|
||||||
|
session=cast(AsyncSession, session),
|
||||||
|
caller_session=None,
|
||||||
|
refresh=(refreshed,),
|
||||||
|
)
|
||||||
|
|
||||||
assert session.commits == 1
|
assert session.commits == 1
|
||||||
assert session.flushes == 0
|
assert session.flushes == 0
|
||||||
@@ -126,11 +141,15 @@ async def test_finalize_commits_for_service_owned_session():
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_finalize_flushes_for_caller_owned_session():
|
async def test_finalize_flushes_for_caller_owned_session():
|
||||||
service = ServiceBase(settings=_settings(), session_factory=object())
|
service = ServiceBase(settings=_settings(), session_factory=_session_factory_stub())
|
||||||
session = _TrackingSession()
|
session = _TrackingSession()
|
||||||
refreshed = object()
|
refreshed = object()
|
||||||
|
|
||||||
await service._finalize(session=session, caller_session=object(), refresh=(refreshed,))
|
await service._finalize(
|
||||||
|
session=cast(AsyncSession, session),
|
||||||
|
caller_session=_session_stub(),
|
||||||
|
refresh=(refreshed,),
|
||||||
|
)
|
||||||
|
|
||||||
assert session.commits == 0
|
assert session.commits == 0
|
||||||
assert session.flushes == 1
|
assert session.flushes == 1
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||||
UI_ROOT = PROJECT_ROOT / "src" / "transcription" / "ui"
|
UI_ROOT = PROJECT_ROOT / "src" / "transcription" / "ui"
|
||||||
API_ROOT = PROJECT_ROOT / "src" / "transcription" / "api"
|
API_ROOT = PROJECT_ROOT / "src" / "transcription" / "api"
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from pathlib import Path
|
|||||||
|
|
||||||
from transcription.config import Settings
|
from transcription.config import Settings
|
||||||
|
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,43 @@ async def test_openrouter_captures_exact_transport_and_secret_safe_manifest():
|
|||||||
assert result.request_manifest.omitted_optional_parameters == ("temperature", "top_p")
|
assert result.request_manifest.omitted_optional_parameters == ("temperature", "top_p")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_openrouter_manifest_uses_configured_commit_not_ambient_env(monkeypatch):
|
||||||
|
monkeypatch.setenv("TRANSCRIPTION_COMMIT", "ambient-value")
|
||||||
|
|
||||||
|
async def handler(request: httpx.Request) -> httpx.Response:
|
||||||
|
return httpx.Response(
|
||||||
|
200,
|
||||||
|
content=(
|
||||||
|
b'{"id":"gen-1","created":1,"model":"vendor/model","object":"chat.completion",'
|
||||||
|
b'"system_fingerprint":null,"choices":[{"index":0,"finish_reason":"stop",'
|
||||||
|
b'"message":{"role":"assistant","content":"Transcript"}}]}'
|
||||||
|
),
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
request=request,
|
||||||
|
)
|
||||||
|
|
||||||
|
provider = OpenRouterTranscriptionProvider(
|
||||||
|
settings=Settings(openrouter_api_key="test-key", transcription_commit="configured-commit"),
|
||||||
|
async_client=httpx.AsyncClient(transport=httpx.MockTransport(handler)),
|
||||||
|
)
|
||||||
|
result = await provider.transcribe(
|
||||||
|
prompt_text="Literal prompt",
|
||||||
|
image_bytes=b"source-bytes",
|
||||||
|
mime_type="image/png",
|
||||||
|
source_reference=SourceEvidenceReference(
|
||||||
|
source_id=uuid4(),
|
||||||
|
digest_sha256=hashlib.sha256(b"source-bytes").hexdigest(),
|
||||||
|
byte_size=len(b"source-bytes"),
|
||||||
|
media_type="image/png",
|
||||||
|
page_number=1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.request_manifest is not None
|
||||||
|
assert result.request_manifest.software.application_commit == "configured-commit"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_openrouter_captures_body_consumed_as_sdk_stream():
|
async def test_openrouter_captures_body_consumed_as_sdk_stream():
|
||||||
response_body = (
|
response_body = (
|
||||||
|
|||||||
Reference in New Issue
Block a user