unstructured-platform-plugins 0.1.0__tar.gz → 0.1.1.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/PKG-INFO +1 -1
- unstructured_platform_plugins-0.1.1.dev1/unstructured_platform_plugins/__init__.py +13 -0
- unstructured_platform_plugins-0.1.1.dev1/unstructured_platform_plugins/__version__.py +1 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/etl_uvicorn/api_generator.py +40 -4
- unstructured_platform_plugins-0.1.1.dev1/unstructured_platform_plugins/invocation_scope.py +63 -0
- unstructured_platform_plugins-0.1.0/unstructured_platform_plugins/__version__.py +0 -1
- unstructured_platform_plugins-0.1.0/unstructured_platform_plugins/etl_uvicorn/__init__.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/.gitignore +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/LICENSE.md +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/README.md +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/pyproject.toml +0 -0
- {unstructured_platform_plugins-0.1.0/unstructured_platform_plugins → unstructured_platform_plugins-0.1.1.dev1/unstructured_platform_plugins/etl_uvicorn}/__init__.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/etl_uvicorn/main.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/etl_uvicorn/otel.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/etl_uvicorn/utils.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/generated/__init__.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/generated/error_audience_v1.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/generated/invocation_context_v1.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/invocation_context.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/invocation_settings.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/schema/__init__.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/schema/filedata_meta.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/schema/json_schema.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/schema/model.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/schema/usage.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/schema/utils.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/type_hints.py +0 -0
- {unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/unstructured_platform_plugins/validate_api.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: unstructured_platform_plugins
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1.dev1
|
|
4
4
|
Summary: Wrapper to convert arbitrary code into a uvicorn/fastapi implementation for Unstructured Platform
|
|
5
5
|
License-File: LICENSE.md
|
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from unstructured_platform_plugins.invocation_scope import (
|
|
2
|
+
InvocationIdentity,
|
|
3
|
+
current_invocation_identity,
|
|
4
|
+
invocation_scratch_dir,
|
|
5
|
+
)
|
|
6
|
+
from unstructured_platform_plugins.invocation_settings import current_invocation_context
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"InvocationIdentity",
|
|
10
|
+
"current_invocation_context",
|
|
11
|
+
"current_invocation_identity",
|
|
12
|
+
"invocation_scratch_dir",
|
|
13
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.1.dev1" # pragma: no cover
|
|
@@ -3,6 +3,7 @@ import hashlib
|
|
|
3
3
|
import inspect
|
|
4
4
|
import json
|
|
5
5
|
import logging
|
|
6
|
+
import re
|
|
6
7
|
from typing import Any, Callable, Optional, Union, get_origin
|
|
7
8
|
|
|
8
9
|
from fastapi import FastAPI, HTTPException, status
|
|
@@ -12,7 +13,7 @@ from pydantic import BaseModel, Field, create_model
|
|
|
12
13
|
from starlette.responses import RedirectResponse
|
|
13
14
|
from typing_extensions import deprecated
|
|
14
15
|
from unstructured_ingest.data_types.file_data import BatchFileData, FileData, file_data_from_dict
|
|
15
|
-
from unstructured_ingest.error import UnstructuredIngestError, UserError
|
|
16
|
+
from unstructured_ingest.error import RateLimitError, UnstructuredIngestError, UserError
|
|
16
17
|
from uvicorn.config import LOG_LEVELS
|
|
17
18
|
from uvicorn.importer import import_from_string
|
|
18
19
|
|
|
@@ -111,19 +112,54 @@ def failure_category_of(error: BaseException) -> Optional[str]:
|
|
|
111
112
|
return category if isinstance(category, str) else None
|
|
112
113
|
|
|
113
114
|
|
|
115
|
+
def _as_error_reason(category: Optional[str]) -> Optional[str]:
|
|
116
|
+
"""Render a failure_category as an ``error.reason`` token, or None when it cannot be.
|
|
117
|
+
|
|
118
|
+
``failure_category`` and ``error.reason`` are two vocabularies, not one: the preflight
|
|
119
|
+
categories are SCREAMING_SNAKE (``AUTH_PERMISSION_DENIED``) while ``error.reason`` is
|
|
120
|
+
specified lower_snake_case. ``failure_category`` still rides its own top-level response
|
|
121
|
+
field verbatim, so nothing is lost by normalizing the copy that lands here.
|
|
122
|
+
|
|
123
|
+
The category is plugin-supplied free text rather than a closed enum, so lowercasing
|
|
124
|
+
alone would not produce a snake_case token; punctuation and whitespace collapse to
|
|
125
|
+
single underscores and a category with no usable characters yields None.
|
|
126
|
+
"""
|
|
127
|
+
if category is None:
|
|
128
|
+
return None
|
|
129
|
+
# -- str.lower, not category.lower: the isinstance check upstream admits str SUBCLASSES,
|
|
130
|
+
# -- and this renders an error response, where raising is the one thing it must not do --
|
|
131
|
+
reason = re.sub(r"[^a-z0-9]+", "_", str.lower(category)).strip("_")
|
|
132
|
+
return reason or None
|
|
133
|
+
|
|
134
|
+
|
|
114
135
|
def plugin_error_of(error: BaseException) -> Optional[PluginErrorMetadata]:
|
|
115
136
|
"""Map the legacy UserError family onto the canonical plugin-error envelope.
|
|
116
137
|
|
|
117
138
|
The ratified ErrorAudience vocabulary owns the wire spelling. A non-user failure remains
|
|
118
139
|
unclassified: orchestrators must not infer actionability from its HTTP status.
|
|
140
|
+
|
|
141
|
+
Terminal is the safe default, so a transient condition has to declare itself: a provider
|
|
142
|
+
rate limit is the one member of this family that clears on its own, and it mirrors the
|
|
143
|
+
utic_plugin_base ``RateLimitError`` declaration (platform-libs #889) field for field.
|
|
144
|
+
Retryability is orthogonal to audience -- a throttled request is still the caller's
|
|
145
|
+
quota, so the audience stays ``user``.
|
|
146
|
+
|
|
147
|
+
The class picks the DEFAULT reason; an explicit ``failure_category`` still wins, on this
|
|
148
|
+
branch as on every other. A plugin that declares a category has said something more
|
|
149
|
+
specific than the class did (a rate limit raised as ``PROVIDER_RATE_LIMITED``), and
|
|
150
|
+
dropping it here would discard the more precise of the two.
|
|
119
151
|
"""
|
|
120
152
|
if not isinstance(error, UserError):
|
|
121
153
|
return None
|
|
154
|
+
if isinstance(error, RateLimitError):
|
|
155
|
+
error_type, default_reason, retryable = "dependency", "rate_limited", True
|
|
156
|
+
else:
|
|
157
|
+
error_type, default_reason, retryable = "configuration", "invalid_input", False
|
|
122
158
|
return PluginErrorMetadata(
|
|
123
|
-
error_type=
|
|
124
|
-
error_reason=failure_category_of(error) or
|
|
159
|
+
error_type=error_type,
|
|
160
|
+
error_reason=_as_error_reason(failure_category_of(error)) or default_reason,
|
|
125
161
|
audience=ErrorAudience.USER,
|
|
126
|
-
retryable=
|
|
162
|
+
retryable=retryable,
|
|
127
163
|
)
|
|
128
164
|
|
|
129
165
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Identity and filesystem scope for the current invocation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
from dataclasses import dataclass, fields
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from uuid import uuid4
|
|
11
|
+
|
|
12
|
+
from unstructured_platform_plugins.invocation_settings import current_invocation_context
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class InvocationIdentity:
|
|
17
|
+
job_id: str | None
|
|
18
|
+
tenant_id: str | None
|
|
19
|
+
org_id: str | None
|
|
20
|
+
workflow_id: str | None
|
|
21
|
+
attribution_id: str | None
|
|
22
|
+
dag_node_id: str | None
|
|
23
|
+
dag_node_type: str | None
|
|
24
|
+
dag_node_subtype: str | None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def current_invocation_identity() -> InvocationIdentity:
|
|
28
|
+
"""Read identity from the bound context, or the environment when no context is bound."""
|
|
29
|
+
context = current_invocation_context()
|
|
30
|
+
values = {}
|
|
31
|
+
for field in fields(InvocationIdentity):
|
|
32
|
+
values[field.name] = (
|
|
33
|
+
getattr(context, field.name, None)
|
|
34
|
+
if context is not None
|
|
35
|
+
else os.getenv(field.name.upper())
|
|
36
|
+
)
|
|
37
|
+
return InvocationIdentity(**values)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _segment(value: str | None) -> str:
|
|
41
|
+
if not value:
|
|
42
|
+
return "unknown"
|
|
43
|
+
safe = re.sub(r"[^a-zA-Z0-9_.-]", "_", value)
|
|
44
|
+
if safe in {".", ".."} or safe != value or len(safe) > 96:
|
|
45
|
+
digest = hashlib.sha256(value.encode()).hexdigest()[:16]
|
|
46
|
+
safe = f"{safe[:79].strip('.') or 'unknown'}-{digest}"
|
|
47
|
+
return safe
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def invocation_scratch_dir(root: str | os.PathLike[str], *, create: bool = True) -> Path:
|
|
51
|
+
"""Return an invocation directory, or the unchanged root when no context is bound."""
|
|
52
|
+
path = Path(root)
|
|
53
|
+
context = current_invocation_context()
|
|
54
|
+
if context is not None:
|
|
55
|
+
identity = current_invocation_identity()
|
|
56
|
+
invocation_id = context.invocation_id
|
|
57
|
+
if not invocation_id and context.record_ids is None and context.invocation_ids is None:
|
|
58
|
+
invocation_id = context.record_id
|
|
59
|
+
path = path / _segment(identity.job_id) / _segment(identity.dag_node_id)
|
|
60
|
+
path = path / _segment(invocation_id or uuid4().hex)
|
|
61
|
+
if create:
|
|
62
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
63
|
+
return path
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.0" # pragma: no cover
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unstructured_platform_plugins-0.1.0 → unstructured_platform_plugins-0.1.1.dev1}/pyproject.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|