Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d6dd4a4
chore: configure mypy with proper environment and ignore missing stubs
archae0pteryx Jan 28, 2026
4bc058a
fix(types): use Any instead of any in predictions.py
archae0pteryx Jan 28, 2026
6769b13
fix(types): add TYPE_CHECKING import for Endpoint
archae0pteryx Jan 28, 2026
25de0df
fix(types): use explicit Optional for open_data parameter
archae0pteryx Jan 28, 2026
409c733
fix(types): use explicit Optional for callback parameter
archae0pteryx Jan 28, 2026
fbad0c4
fix(types): use explicit Optional for open_data parameters
archae0pteryx Jan 28, 2026
95af785
fix(types): add generic typing to run_coro_thread_save
archae0pteryx Jan 28, 2026
cea39a8
fix(types): resolve type errors in data_syncify.py
archae0pteryx Jan 28, 2026
fa8ee38
fix(types): resolve type errors in worker_syncify.py
archae0pteryx Jan 28, 2026
b777790
fix(types): resolve Pydantic discriminated union inheritance
archae0pteryx Jan 28, 2026
9d6c9ea
fix(types): fix unreachable code and variable reuse in visualize.py
archae0pteryx Jan 28, 2026
a9bb80f
fix(types): resolve type errors in jobs, endpoint, and data modules
archae0pteryx Jan 28, 2026
dc797bc
fix(types): resolve type errors in worker_endpoint and client_session
archae0pteryx Jan 28, 2026
c1adc7a
fix(types): resolve all remaining type errors
archae0pteryx Jan 28, 2026
02092fa
chore: remove error suppression from typecheck task
archae0pteryx Jan 28, 2026
2faa88a
chore: enable ruff linting for full codebase
archae0pteryx Jan 28, 2026
505cf8a
chore: update uv.lock with pandas-stubs dependency
archae0pteryx Jan 28, 2026
903bf00
Fix lint errors: remove unused imports and rename unused loop variable
archae0pteryx Jan 28, 2026
649a1ab
Fix lint errors in annotations.py and data_endpoint.py
archae0pteryx Jan 28, 2026
449a5d6
Fix remaining lint errors for CI
archae0pteryx Jan 28, 2026
1ecb429
Fix import sorting in data_jobs.py and data_syncify.py
archae0pteryx Jan 28, 2026
311a39e
Fix pyright type errors
archae0pteryx Jan 28, 2026
ceb31d9
Add pyright config to suppress false positives for Pydantic discrimin…
archae0pteryx Jan 28, 2026
ce4d3e5
Add inline pyright ignore comments for Pydantic discriminated union t…
archae0pteryx Jan 28, 2026
2065cf4
Clean up redundant config and fix typos
archae0pteryx Jan 28, 2026
a6a391d
Add TODO.md to track technical debt
archae0pteryx Jan 28, 2026
1ab7ae7
Move pyright config to pyproject.toml for CI compatibility
archae0pteryx Jan 28, 2026
a9519e6
Fix Pydantic discriminated union pattern properly
archae0pteryx Jan 28, 2026
1c17af9
Fix duplicate CI runs on PR pushes
archae0pteryx Jan 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: Checks and tests

on:
push:
branches:
- main
pull_request:
branches:
- main
Expand Down
2 changes: 1 addition & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ tasks:

typecheck:
cmds:
- uvx mypy eyepop || true
- uv run mypy eyepop

lint:
desc: "Run ruff on changed files (vs main branch)"
Expand Down
371 changes: 371 additions & 0 deletions example.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,371 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "0ad595a0-ec5d-488a-aa8b-6a317d4f0c55",
"metadata": {},
"outputs": [],
"source": [
"%pip install eyepop==3.9.10"
]
},
{
"cell_type": "markdown",
"id": "61a87acf-3094-45fc-8b64-a31da6a5b124",
"metadata": {},
"source": [
"## Setting authentication variables"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fd4f56d1693d320b",
"metadata": {},
"outputs": [],
"source": [
"import getpass\n",
"\n",
"EYEPOP_ACCOUNT_ID=\"7587fa529fad497c8965d7b6a81a0010\"\n",
"EYEPOP_API_KEY=\"eyp_9b33b6bf30501dce141ef92c02e4c14ab608facad9e0dc46bd43b7f18d2e2872a39c44c72bb9bac87e31ccec1cc057128b5e872fa0db793bce7b86dd9a3b4dc3\""
]
},
{
"cell_type": "markdown",
"id": "338694d36d95eeed",
"metadata": {},
"source": [
"## Setting a namespace prefix\n",
"All aliases for abilities will be created with this prefix. NOTE: the account must have a namespace prefix registered so that this values starts with the registered prefix. E.g. if you account has a namespace prefix registered like `foo.bar` you can use here `foo.bar` or `foo.bar.baz`. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b6c4edac67a78c72",
"metadata": {},
"outputs": [],
"source": [
"NAMESPACE_PREFIX=\"carsandbids\""
]
},
{
"cell_type": "markdown",
"id": "74370bb4-965d-45b6-a856-e746680bab81",
"metadata": {},
"source": [
"## Defining the abilities\n",
"This just defines the list of abilities as prototypes. The registration happens below."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "df78785f-26f1-4304-8a52-fd3706828514",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"from eyepop import EyePopSdk\n",
"from eyepop.data.data_types import InferRuntimeConfig, VlmAbilityGroupCreate, VlmAbilityCreate, TransformInto\n",
"from eyepop.worker.worker_types import CropForward, ForwardComponent, FullForward, InferenceComponent, Pop\n",
"import json\n",
"\n",
"ability_prototypes = [\n",
" VlmAbilityCreate(\n",
" name=f\"{NAMESPACE_PREFIX}.image-classify.primary-shot-type\",\n",
" description=\"\",\n",
" worker_release=\"qwen3-instruct\",\n",
" text_prompt=\"Determine the primary content of this image and assign exactly one label: Exterior, Interior, Document, or Other. Choose Exterior only if the full or mostly full exterior of the vehicle is visible in a standard photo. Choose Interior if the vehicle's cabin is visible. Choose Document if the image contains documents or forms with no vehicle visible. Choose Other if the image shows a close-up detail of the vehicle, a partial component, the undercarriage, or anything that does not meet the definitions above. Return only the label.\",\n",
" transform_into=TransformInto(\n",
" classes=[\n",
" \"Exterior\", \n",
" \"Interior\", \n",
" \"Document\", \n",
" \"Other\"\n",
" ]\n",
" ),\n",
" config=InferRuntimeConfig(\n",
" max_new_tokens=5,\n",
" image_size=512\n",
" ),\n",
" is_public=False\n",
" ),\n",
" VlmAbilityCreate(\n",
" name=f\"{NAMESPACE_PREFIX}.image-classify.subtype.exterior\",\n",
" description=\"\",\n",
" worker_release=\"qwen3-instruct\",\n",
" text_prompt=\"Determine the exterior shot type for this vehicle image. The image is guaranteed to be an Exterior view. Select exactly one label from the following list and return only that label: front profile, front ¾ view, side profile, rear ¾ view, rear profile, convertible top up, convertible top down. Do not infer mirrored or reflected views; do not infer hidden parts or multiple views; do not return more than one label; do not include explanations or additional text. Return only the label exactly as written above.\",\n",
" transform_into=TransformInto(\n",
" classes=[\n",
" \"front profile\",\n",
" \"front ¾ view\",\n",
" \"side profile\",\n",
" \"rear ¾ view\",\n",
" \"rear profile\",\n",
" \"convertible top up\",\n",
" \"convertible top down\"\n",
" ]\n",
" ),\n",
" config=InferRuntimeConfig(\n",
" max_new_tokens=5,\n",
" image_size=512\n",
" ),\n",
" is_public=False\n",
" ),\n",
" VlmAbilityCreate(\n",
" name=f\"{NAMESPACE_PREFIX}.image-classify.subtype.exterior-left-right\",\n",
" description=\"\",\n",
" worker_release=\"qwen3-instruct\",\n",
" text_prompt=\"Is the front of this vehicle pointed to the left or right of the image? Respond with LEFT or RIGHT only.\",\n",
" transform_into=TransformInto(\n",
" classes=[\n",
" \"LEFT\",\n",
" \"RIGHT\",\n",
" ]\n",
" ),\n",
" config=InferRuntimeConfig(\n",
" max_new_tokens=5,\n",
" image_size=512\n",
" ),\n",
" is_public=False\n",
" ),\n",
" VlmAbilityCreate(\n",
" name=f\"{NAMESPACE_PREFIX}.image-classify.subtype.document-ocr-title\",\n",
" description=\"\",\n",
" worker_release=\"qwen3-instruct\",\n",
" text_prompt=\"Transcribe the text in this image. Find the following fields and output in JSON format:\\ndocument_type\\nstate_two_letter\\ncertificate_number\\nvehicle_identification_number\\nvehicle_type\\nowner_name\\nowner_address\\ndate_of_issue\\ndate_of_transfer\\ntitle_number\\ntitle_type\\nlien_info\\ntitle_brand\\nrespond only with json\",\n",
" transform_into=TransformInto(),\n",
" config=InferRuntimeConfig(\n",
" max_new_tokens=200,\n",
" image_size=1024\n",
" ),\n",
" is_public=False\n",
" ) \n",
"]"
]
},
{
"cell_type": "markdown",
"id": "7b2c85f0-5812-481a-9ef3-1ab8a2076ab1",
"metadata": {},
"source": [
"## Listing current abilities in this account"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cac73f67-eb85-4b89-a80e-a7ab00d4c45f",
"metadata": {},
"outputs": [],
"source": [
"with EyePopSdk.dataEndpoint(api_key=EYEPOP_API_KEY, account_id=EYEPOP_ACCOUNT_ID, eyepop_url=\"https://compute.eyepop.ai\") as endpoint:\n",
" vlm_ability_groups = endpoint.list_vlm_ability_groups()\n",
" print(f'found {len(vlm_ability_groups)} active ability groups in account {EYEPOP_ACCOUNT_ID}')\n",
" for vlm_ability_group in vlm_ability_groups:\n",
" vlm_abilities = endpoint.list_vlm_abilities(vlm_ability_group_uuid=vlm_ability_group.uuid)\n",
" print(f'\\nfound {len(vlm_abilities)} active abilities in group {vlm_ability_group.name} ({vlm_ability_group.uuid}):')\n",
" for vlm_ability in vlm_abilities:\n",
" aliases = '.'.join([f'{entry.alias}:{entry.tag}' for entry in vlm_ability.alias_entries])\n",
" print(f'\\t{vlm_ability.name} ({vlm_ability.uuid}): status={vlm_ability.status}, aliases=[{aliases}]')"
]
},
{
"cell_type": "markdown",
"id": "1f97dde6-0f0a-4163-8094-2273874f75b9",
"metadata": {},
"source": [
"## DELETE all abilities in this account"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "89a7338f-a127-4a4d-b4d9-7d6cd58d219e",
"metadata": {},
"outputs": [],
"source": [
"with EyePopSdk.dataEndpoint(api_key=EYEPOP_API_KEY, account_id=EYEPOP_ACCOUNT_ID, eyepop_url=\"https://compute.eyepop.ai\") as endpoint:\n",
" vlm_ability_groups = endpoint.list_vlm_ability_groups()\n",
" print(f'found {len(vlm_ability_groups)} active ability groups in account {EYEPOP_ACCOUNT_ID}')\n",
" for vlm_ability_group in vlm_ability_groups:\n",
" endpoint.delete_vlm_ability_group(vlm_ability_group.uuid)\n",
" print(f'\\tDELETED {vlm_ability_group.name} / {vlm_ability_group.uuid}')\n",
" vlm_abilities = endpoint.list_vlm_abilities()\n",
" print(f'found {len(vlm_abilities)} active abilities in account {EYEPOP_ACCOUNT_ID}:')\n",
" for vlm_ability in vlm_abilities:\n",
" endpoint.delete_vlm_ability(vlm_ability.uuid)\n",
" print(f'\\tDELETED {vlm_ability.name} / {vlm_ability.uuid}')"
]
},
{
"cell_type": "markdown",
"id": "19394615-af74-498a-86da-4bc533359405",
"metadata": {},
"source": [
"## Create all abilities, groups and aliases "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7c2355f2-0937-47a6-b2a9-7053a3e535b1",
"metadata": {},
"outputs": [],
"source": [
"with EyePopSdk.dataEndpoint(api_key=EYEPOP_API_KEY, account_id=EYEPOP_ACCOUNT_ID, eyepop_url=\"https://compute.eyepop.ai\") as endpoint:\n",
" for ability_prototype in ability_prototypes:\n",
" ability_group = endpoint.create_vlm_ability_group(VlmAbilityGroupCreate(\n",
" name=ability_prototype.name,\n",
" description=ability_prototype.description,\n",
" default_alias_name=ability_prototype.name,\n",
" ))\n",
" ability = endpoint.create_vlm_ability(\n",
" create=ability_prototype,\n",
" vlm_ability_group_uuid=ability_group.uuid,\n",
" )\n",
" ability = endpoint.publish_vlm_ability(\n",
" vlm_ability_uuid=ability.uuid,\n",
" alias_name=ability_prototype.name,\n",
" )\n",
" ability = endpoint.add_vlm_ability_alias(\n",
" vlm_ability_uuid=ability.uuid,\n",
" alias_name=ability_prototype.name,\n",
" tag_name=\"latest\"\n",
" )\n",
" print(f\"created ability {ability.uuid} with alias entries {ability.alias_entries}\")"
]
},
{
"cell_type": "markdown",
"id": "7fdf52fb-d613-4eb9-befa-a60c9af85460",
"metadata": {},
"source": [
"## Define some example image Urls\n",
"<p>\n",
" <img src=\"https://media.istockphoto.com/id/2187504402/photo/jaguar-f-type-svr-sports-car.jpg?s=612x612&w=0&k=20&c=NdGWdV6tWIwl95k2fw-LxVcn0ZBR73h08-7aYsrChfQ=\" alt=\"drawing\" width=\"200\"/>\n",
" <img src=\"https://preview.redd.it/current-copy-paste-of-modern-car-interior-multiple-pictures-v0-mg1buk1pcsh81.jpg?width=640&crop=smart&auto=webp&s=ac0ed732dfd82b79e16317fbf6c092d2d69cc780\" alt=\"drawing\" width=\"200\"/>\n",
" <img src=\"https://www.factorywarrantylist.com/uploads/3/3/9/4/3394652/editor/8539916.jpg?1575678451\" alt=\"drawing\" width=\"200\"/>\n",
"</p>\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1edf2c33-e961-4797-925b-9148a7b1044e",
"metadata": {},
"outputs": [],
"source": [
"example_urls = [\n",
" \"https://media.istockphoto.com/id/2187504402/photo/jaguar-f-type-svr-sports-car.jpg?s=612x612&w=0&k=20&c=NdGWdV6tWIwl95k2fw-LxVcn0ZBR73h08-7aYsrChfQ=\",\n",
" \"https://preview.redd.it/current-copy-paste-of-modern-car-interior-multiple-pictures-v0-mg1buk1pcsh81.jpg?width=640&crop=smart&auto=webp&s=ac0ed732dfd82b79e16317fbf6c092d2d69cc780\",\n",
" \"https://www.factorywarrantylist.com/uploads/3/3/9/4/3394652/editor/8539916.jpg?1575678451\"\n",
"]"
]
},
{
"cell_type": "markdown",
"id": "600c463505514c92",
"metadata": {},
"source": [
"## Run individual inference via Pop"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "850127c3-e559-4e60-9c31-39dbc25f3851",
"metadata": {},
"outputs": [],
"source": [
"pop = Pop(components=[\n",
" InferenceComponent(\n",
" ability=f\"{NAMESPACE_PREFIX}.image-classify.primary-shot-type:latest\"\n",
" )\n",
"]) \n",
"with EyePopSdk.workerEndpoint(api_key=EYEPOP_API_KEY, eyepop_url=\"https://compute.eyepop.ai\") as endpoint:\n",
" endpoint.set_pop(pop)\n",
" for url in example_urls:\n",
" job = endpoint.load_from(url)\n",
" while result := job.predict():\n",
" print(json.dumps(result, indent=2))\n",
" print(\"done\")"
]
},
{
"cell_type": "markdown",
"id": "34bf162a-5608-4e20-90dc-4ad6b4793425",
"metadata": {},
"source": [
"## Run the full composable Pop"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "74335084-0bbc-4d21-af81-23f8901ca912",
"metadata": {},
"outputs": [],
"source": [
"pop = Pop(components=[\n",
" InferenceComponent(\n",
" ability=f\"{NAMESPACE_PREFIX}.image-classify.primary-shot-type:latest\",\n",
" forward=FullForward(\n",
" targets=[ForwardComponent(\n",
" forward=FullForward(\n",
" includeClasses=[\"Exterior\"],\n",
" targets=[InferenceComponent(\n",
" ability=f\"{NAMESPACE_PREFIX}.image-classify.subtype.exterior:latest\",\n",
" categoryName=\"subtype_exterior\"\n",
" ),\n",
" InferenceComponent(\n",
" ability=f\"{NAMESPACE_PREFIX}.image-classify.subtype.exterior-left-right:latest\",\n",
" categoryName=\"subtype_exterior_orientation\"\n",
" )]\n",
" ) \n",
" ), ForwardComponent(\n",
" forward=FullForward(\n",
" includeClasses=[\"Document\"],\n",
" targets=[InferenceComponent(\n",
" ability=f\"{NAMESPACE_PREFIX}.subtype.document-ocr-title:latest\",\n",
" categoryName=\"document_ocr\"\n",
" )]\n",
" ) \n",
" )]\n",
" )\n",
" )\n",
"]) \n",
"with EyePopSdk.workerEndpoint(api_key=EYEPOP_API_KEY) as endpoint:\n",
" endpoint.set_pop(pop)\n",
" for url in example_urls:\n",
" job = endpoint.load_from(url)\n",
" while result := job.predict():\n",
" print(json.dumps(result, indent=2))"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
2 changes: 1 addition & 1 deletion eyepop/client_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ async def request_with_retry(
data: Any = None,
content_type: str | None = None,
timeout: aiohttp.ClientTimeout | None = None,
) -> aiohttp.client._RequestContextManager:
) -> aiohttp.ClientResponse:
raise NotImplementedError
Loading
Loading