File size: 6,825 Bytes
ed962ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# -------------------------------------------------------------------
# This source file is available under the terms of the
# Pimcore Open Core License (POCL)
# Full copyright and license information is available in
# LICENSE.md which is distributed with this source code.
#
#  @copyright  Copyright (c) Pimcore GmbH (https://www.pimcore.com)
#  @license    Pimcore Open Core License (POCL)
# -------------------------------------------------------------------

"""Request/response samples for the embedding endpoints, kept out of `main.py` so the routing
stays readable.

These are meant to be *runnable*: pick one in the `/docs` "Try it out" dropdown and send it as-is.
That is why the models named here are small, standard-architecture ones rather than the multi-GB
examples elsewhere in the docs, and why the image sample carries a real (tiny) PNG instead of a
`"base64_encoded_image_string"` placeholder. `tests/test_openapi_examples.py` validates every
sample against the request model, so a sample cannot drift out of sync with the schema.
"""

from typing import Any, Union

# A real 16x16 PNG, so the image samples below can be executed unmodified.
SAMPLE_PNG_BASE64 = (
    "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGUlEQVR4nGNUTX7NQApgIkn1qIZRDUNKAwCg0AGTz0Gc9wAAAABJRU5ErkJggg=="
)

# ~90 MB, no custom code, downloads in seconds — the right default for a first request.
_SMALL_TEXT_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
_SMALL_IMAGE_MODEL = "google/siglip-base-patch16-224"

TEXT_EMBEDDING_EXAMPLES: dict[str, Any] = {
    "single": {
        "summary": "One string (start here)",
        "description": (
            f"Smallest useful request. `{_SMALL_TEXT_MODEL}` is a few hundred MB and needs no "
            "custom code, so it loads on first call without any configuration."
        ),
        "value": {"model": _SMALL_TEXT_MODEL, "input": "a blue sports car"},
    },
    "batch": {
        "summary": "Several strings in one call",
        "description": (
            "A list is embedded in a single forward pass, which is markedly faster than one request "
            "per string. `data` comes back in input order, each entry carrying its `index`."
        ),
        "value": {
            "model": _SMALL_TEXT_MODEL,
            "input": ["a blue sports car", "ein blaues Sportauto", "a recipe for onion soup"],
        },
    },
    "instruction_prefixed": {
        "summary": "Nomic, with its task prefix",
        "description": (
            "Models in the Nomic family expect an instruction prefix on every input — "
            "`search_query: ` for the thing being searched with, `search_document: ` for the things "
            "being searched over. The service does not add it for you; embed queries and documents "
            "with matching prefixes or the two spaces will not line up. This model carries custom "
            "code, so its namespace must appear in `TRUSTED_CODE_ORGS` (it does by default)."
        ),
        "value": {
            "model": "nomic-ai/nomic-embed-text-v1.5",
            "input": ["search_query: a blue sports car"],
        },
    },
    "openai_compatible": {
        "summary": "With the OpenAI compatibility fields",
        "description": (
            "`encoding_format` selects the wire format and only `'float'` is supported here; `user` "
            "is an identifier you invent for abuse attribution and is accepted but ignored. Both "
            "exist so a client written against OpenAI's /v1/embeddings works unchanged. Sending "
            "`encoding_format: 'base64'` or any `dimensions` value is refused with a 422 rather "
            "than silently ignored."
        ),
        "value": {
            "model": _SMALL_TEXT_MODEL,
            "input": "a blue sports car",
            "encoding_format": "float",
            "user": "pimcore-bpt-tenant-42",
        },
    },
}

IMAGE_EMBEDDING_EXAMPLES: dict[str, Any] = {
    "single": {
        "summary": "One base64 image (runnable as-is)",
        "description": (
            "The `input` value is the base64 of the image bytes. The sample below is a real 16x16 "
            "PNG, so this request works unmodified — swap in your own base64 to try a real asset."
        ),
        "value": {"model": _SMALL_IMAGE_MODEL, "input": SAMPLE_PNG_BASE64},
    },
    "batch": {
        "summary": "Several images in one call",
        "description": (
            "Images are embedded in one forward pass. An image that cannot be decoded is skipped: "
            "it is omitted from `data`, so its `index` is missing rather than shifting the rest."
        ),
        "value": {"model": _SMALL_IMAGE_MODEL, "input": [SAMPLE_PNG_BASE64, SAMPLE_PNG_BASE64]},
    },
    "data_url": {
        "summary": "A data: URL",
        "description": (
            "A `data:image/...;base64,` prefix is accepted and stripped, so values copied straight "
            "out of a browser or an HTML `src` attribute work without editing."
        ),
        "value": {"model": _SMALL_IMAGE_MODEL, "input": f"data:image/png;base64,{SAMPLE_PNG_BASE64}"},
    },
}

# Shared by both endpoints — the response shape is identical.
# `Union[int, str]` rather than `int | str`: module-level annotations are evaluated at runtime, and
# PEP 604 unions need Python 3.10 — the Dockerfile and CI both run 3.9.
EMBEDDINGS_RESPONSES: dict[Union[int, str], dict[str, Any]] = {
    200: {
        "description": "Embeddings in input order. Every vector is L2-normalized (length 1).",
        "content": {
            "application/json": {
                "example": {
                    "object": "list",
                    "data": [
                        {"object": "embedding", "index": 0, "embedding": [0.0163, -0.0412, 0.0967]},
                        {"object": "embedding", "index": 1, "embedding": [-0.0271, 0.0538, 0.0114]},
                    ],
                    "model": _SMALL_TEXT_MODEL,
                    "usage": {"prompt_tokens": 0, "total_tokens": 0},
                }
            }
        },
    },
    400: {
        "description": "The `model` value is not a well-formed Hugging Face repo id.",
        "content": {
            "application/json": {
                "example": {
                    "detail": (
                        "Invalid model id '../../etc/passwd'. Expected a Hugging Face repo id such "
                        "as 'namespace/model' or a canonical model name."
                    )
                }
            }
        },
    },
    404: {
        "description": "The model id is well-formed but could not be loaded (unknown, gated, or "
        "unavailable offline).",
        "content": {
            "application/json": {"example": {"detail": "Model 'acme/does-not-exist' could not be loaded: ..."}}
        },
    },
}