Add Semantic PDF Image Extractor skill - #289
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new submission under submissions/semantic-pdf-image-extractor/ that defines the Semantic PDF Image Extractor Agent Skill, including its agent-facing workflow, human-facing gallery README, reference contracts, and an optional deterministic Python helper for rendering/cropping/validation/packaging.
Changes:
- Introduces the agent-facing
SKILL.mdwith an end-to-end extraction workflow and required ZIP output structure. - Adds reference contracts (region proposals, extraction profiles) and a JSON Schema describing the normalized manifest format.
- Provides an optional Python helper (
pdf_image_extractor.py) implementing rendering, cropping, duplicate suggestions, manifest validation, and packaging, plus a dependency-free self-test.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| submissions/semantic-pdf-image-extractor/SKILL.md | Defines the runtime workflow, output archive structure, and security/privacy constraints for the skill. |
| submissions/semantic-pdf-image-extractor/scripts/pdf_image_extractor.py | Optional deterministic helper for rendering/cropping/validation/dedup/packaging and self-test. |
| submissions/semantic-pdf-image-extractor/references/region-proposals.md | Specifies the normalized region-proposals contract consumed by the helper and workflow. |
| submissions/semantic-pdf-image-extractor/references/output-schema.json | JSON Schema for manifest.json output structure. |
| submissions/semantic-pdf-image-extractor/references/extraction-profiles.md | Scenario-driven profiles for inclusion/context tuning while keeping a consistent manifest. |
| submissions/semantic-pdf-image-extractor/README.md | Human-facing gallery overview, requirements, and example requests. |
| submissions/semantic-pdf-image-extractor/metadata.json | Submission metadata (platforms/tags/author/version) for the gallery/import pipeline. |
| submissions/semantic-pdf-image-extractor/assets/manifest-template.json | Starter manifest template matching the schema and workflow. |
Suppressed comments (1)
submissions/semantic-pdf-image-extractor/scripts/pdf_image_extractor.py:287
duplicateGroupIdis marked as required inreferences/output-schema.json, but the validator treats a missingduplicateGroupIdthe same as an explicitnull(because it usesasset.get(...)). This meansvalidate_manifest_data()can accept manifests that do not actually conform to the published schema.
duplicate_group_id = asset.get("duplicateGroupId")
if duplicate_group_id is not None:
require_id(duplicate_group_id, f"{field}.duplicateGroupId")
asset_group_ids[asset_id] = duplicate_group_id
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| for range_index, page_range in enumerate(request["pageRanges"]): | ||
| field = f"request.pageRanges[{range_index}]" | ||
| require(isinstance(page_range, dict), f"{field} must be an object") | ||
| document_id = require_id(page_range.get("documentId"), f"{field}.documentId") | ||
| require(document_id in document_ids, f"{field}.documentId is unknown") | ||
| start, end = page_range.get("from"), page_range.get("to") | ||
| require(isinstance(start, int) and not isinstance(start, bool) and start >= 1, | ||
| f"{field}.from is invalid") | ||
| require(isinstance(end, int) and not isinstance(end, bool) and end >= start, | ||
| f"{field}.to is invalid") |
| require(manifest.get("schemaVersion") == SCHEMA_VERSION, f"schemaVersion must be {SCHEMA_VERSION}") | ||
| generated_at = manifest.get("generatedAt") | ||
| if generated_at is not None: | ||
| require(isinstance(generated_at, str), "generatedAt must be null or an ISO timestamp") |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Suppressed comments (5)
submissions/semantic-pdf-image-extractor/scripts/pdf_image_extractor.py:183
validate_manifest_dataallowsgeneratedAtto be omitted entirely (becausemanifest.get('generatedAt')returnsNone), butoutput-schema.jsonrequires thegeneratedAtproperty (nullable). This can let manifests pass helper validation while still failing schema validation downstream.
require(isinstance(manifest, dict), "Manifest root must be an object")
require(manifest.get("schemaVersion") == SCHEMA_VERSION, f"schemaVersion must be {SCHEMA_VERSION}")
generated_at = manifest.get("generatedAt")
if generated_at is not None:
require(isinstance(generated_at, str), "generatedAt must be null or an ISO timestamp")
submissions/semantic-pdf-image-extractor/scripts/pdf_image_extractor.py:249
- Page entries should include
widthandheightkeys (nullable) peroutput-schema.json, but the validator currently treats a missing key the same asnull(page.get(dimension)), so a schema-invalid page object can still pass helper validation.
for dimension in ("width", "height"):
value = page.get(dimension)
require(value is None or (isinstance(value, int) and not isinstance(value, bool) and value >= 1),
f"{page_field}.{dimension} must be null or a positive integer")
submissions/semantic-pdf-image-extractor/scripts/pdf_image_extractor.py:263
request.pageRangesvalidation does not ensurefrom/tostay within the referenced document’s declaredpageCount. This can produce a manifest that passes helper validation but describes impossible page ranges.
start, end = page_range.get("from"), page_range.get("to")
require(isinstance(start, int) and not isinstance(start, bool) and start >= 1,
f"{field}.from is invalid")
require(isinstance(end, int) and not isinstance(end, bool) and end >= start,
f"{field}.to is invalid")
submissions/semantic-pdf-image-extractor/scripts/pdf_image_extractor.py:287
duplicateGroupIdis marked as a required (nullable) property inoutput-schema.json, but the validator does not require the key to exist (a missing key is treated asNone). This can allow schema-invalid assets to pass helper validation.
duplicate_group_id = asset.get("duplicateGroupId")
if duplicate_group_id is not None:
require_id(duplicate_group_id, f"{field}.duplicateGroupId")
asset_group_ids[asset_id] = duplicate_group_id
submissions/semantic-pdf-image-extractor/scripts/pdf_image_extractor.py:156
validate_qualitychecks field values but does not require the JSON Schema required keys to be present (e.g., a manifest could omitwidth/fileBytesentirely and still pass). Sinceoutput-schema.jsonmarks these fields as required, the validator should explicitly enforce key presence (null is fine when allowed) before validating types/values.
This issue also appears in the following locations of the same file:
- line 179
- line 246
- line 259
- line 284
def validate_quality(value: Any, field: str, image_path: Path | None = None) -> None:
require(isinstance(value, dict), f"{field} must be an object")
for key in ("width", "height"):
item = value.get(key)
require(item is None or (isinstance(item, int) and not isinstance(item, bool) and item >= 1),
Summary
Adds Semantic PDF Image Extractor, a domain-neutral Agent Skill for finding and extracting meaningful photos, diagrams, charts, maps, screenshots, and other visual evidence from PDFs.
The workflow renders pages before semantic analysis, preserves captions and page context, records normalized provenance coordinates, suggests exact and near-duplicate groups, and marks uncertain assets for review. It supports flattened scans and mixed PDF layouts rather than relying only on embedded image objects.
Included
SKILL.mdREADME.mdPlatform and dependencies
pypdfium2andPillowenable helper-based rendering and croppingPrivacy and security
Validation
pdf, images, ZIPs, caches, and bytecode absent)npm run check:submissionspassed (91 submissions)npm run import:submissionspassednpm run buildpassed (390 pages)submissions/semantic-pdf-image-extractor/Licensing
I agree that this contribution is made under the repository's MIT License.