Observed since 21 Oct, 10:03 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Bridge for LLM"s
A unified Python SDK for working with multiple AI providers through a consistent set of utilities — text generation (sync + streaming), prompt management, reusable variables, structured outputs, queue-backed async execution, image generation, video generation — all with client-side validation, typed exceptions, and provider-normalized error handling.
generate_stream) on every provider that supports itexecute_prompt_function_calling) for tool use across providersBadRequestException, AuthenticationException, RateLimitException, TimeoutException, ServerException)BaseValidator, providers override only what differsopenai.BadRequestError: Error code: 400 - {'error': {...}} so they're immediately useful in tracebacks@provider_wrapper)pip install aibridgecore
Python 3.9+. Set the config file path:
export AIBRIDGE_CONFIG=/absolute/path/to/aibridge_config.yaml
Minimal aibridge_config.yaml:
open_ai:
equal:
- YOUR_OPENAI_API_KEY
kimi:
equal:
- YOUR_KIMI_API_KEY
anthropic:
equal:
- YOUR_ANTHROPIC_API_KEY
database: sql # or "mongo"
message_queue: redis
redis_host: localhost
redis_port: 6379
Each provider has its own top-level key. The equal list supports key rotation across multiple keys.
from aibridgecore.ai_services.openai_services import OpenAIService
from aibridgecore.exceptions import AIBridgeException
try:
response = OpenAIService.generate(
prompts=["Summarise this paragraph in 50 words..."],
model="gpt-3.5-turbo",
variation_count=1,
max_tokens=200,
temperature=0.5,
)
print(response)
except AIBridgeException as e:
print(f"{type(e).__name__}: {e.user_message}")
Every exception in the SDK is a subclass of AIBridgeException and carries the same attributes: e.status_code (int HTTP status), e.code (string error code), e.category, e.message, e.user_message, e.param, e.expected, e.received, e.provider, e.retryable, e.developer_message, and the full flat e.details dict (plus e.to_dict()).
┌─────────────────────────────────────────────────────────────┐
│ Caller (script / Flask / FastAPI / worker) │
└────────────────────┬────────────────────────────────────────┘
│ OpenAIService.generate(prompts=..., ...)
▼
┌─────────────────────────────────────────────────────────────┐
│ @provider_wrapper(validator=OpenAIValidator, ...) │
│ 1. OpenAIValidator.validate_generate(kwargs) │
│ 2. func(*args, **kwargs) if validation passes │
│ 3. On any exception → normalize → re-raise typed │
└────────────────────┬────────────────────────────────────────┘
│ HTTPS (only if validation passed)
▼
┌─────────────────────────────────────────────────────────────┐
│ Provider API (OpenAI, Anthropic, Kimi, ...) │
└─────────────────────────────────────────────────────────────┘
Three pieces do the heavy lifting:
| Piece | File | Responsibility |
|---|---|---|
| Validators | aibridgecore/validators/provider_validator.py | Check kwargs before the call. Raise BadRequestException with rich details on bad input. |
| Wrapper | aibridgecore/wrappers/provider_wrapper.py | Run the validator, run the function, catch any exception, transform it into the right category class. |
| Normalizer | aibridgecore/error_hanldling/error_normalizer.py | Inspect any non-typed exception and produce a rich details dict. |
Every provider exposes the same method signature for generate():
SomeService.generate(
prompts=[...], # or prompt_ids=[...] (from stored prompts)
prompt_data=[...], # variable substitutions
variables=[...],
output_format=["json"], # "json", "xml", "csv", "string"
format_strcture=["..."], # JSON schema / XML template / CSV header
model="...",
variation_count=1,
max_tokens=200,
temperature=0.5,
message_queue=False, # if True, enqueue and return response_id
api_key=None, # falls back to AIBRIDGE_CONFIG
output_format_parse=True,
context=[{"role": "system", "context": "..."}],
)
Swap the service class to switch providers:
from aibridgecore.ai_services.openai_services import OpenAIService
from aibridgecore.ai_services.anthropic_ai import AnthropicService
from aibridgecore.ai_services.geminin_services import GeminiAIService
from aibridgecore.ai_services.cohere_llm import CohereApi
from aibridgecore.ai_services.kimi_services import KimiService
from aibridgecore.ai_services.grok_services import GrokService
from aibridgecore.ai_services.mistral_services import MistralService
from aibridgecore.ai_services.deepseek_services import DeepseekService
from aibridgecore.ai_services.alibaba_services import AlibabaService
from aibridgecore.ai_services.ai21labs_text import AI21labsText
from aibridgecore.ai_services.ollama_services import OllamaService
The validation rules and exception types are uniform across all of them.
from aibridgecore.ai_services.openai_services import OpenAIService
from aibridgecore.exceptions import AIBridgeException
try:
stream = OpenAIService.generate_stream(
prompts=["Tell me a 3-sentence story."],
model="gpt-3.5-turbo",
temperature=0.5,
max_tokens=200,
)
for chunk in stream:
if chunk["type"] == "content":
print(chunk["data"], end="", flush=True)
elif chunk["type"] == "usage":
print(f"\n[input={chunk['input_tokens']} output={chunk['output_tokens']}]")
except AIBridgeException as e:
print(f"\n{type(e).__name__}: {e}")
Chunks come in two shapes:
{"type": "content", "data": "<text fragment>"}{"type": "usage", "input_tokens": N, "output_tokens": M} (at the end)Validation runs before the generator is created, so bad input raises a BadRequestException immediately (no API round-trip). Errors raised mid-stream are also caught by @provider_wrapper and normalized into the right typed exception, just like non-streaming methods. ollama_services.py does not expose generate_stream.
result = OpenAIService.execute_prompt_function_calling(
api_key="sk-...",
model="gpt-4",
messages=[{"role": "user", "content": "What's the weather in Paris?"}],
n=1,
functions_call=[{
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}],
max_tokens=200,
temperature=0.3,
)
The validator checks: api_key required, model required and string, messages must be a non-empty list of dicts, n is a positive integer, functions_call is a list of dicts.
Supported on every LLM provider except AI21.
Pass output_format and format_strcture to constrain provider responses to machine-parseable shapes:
response = OpenAIService.generate(
prompts=["List 3 fruits with their colors."],
model="gpt-4",
variation_count=1,
output_format=["json"],
format_strcture=['{"fruits": [{"name": "string", "color": "string"}]}'],
)
Allowed output_format values: "json", "xml", "csv", "string". Lengths of output_format and format_strcture must match len(prompts). JSON templates are checked for parse-ability; XML/CSV templates are sanity-checked at validation time.
Parsers live in aibridgecore/output_validation/.
Store frequently-used prompts in your database and reference them by ID, with placeholder substitution:
response = OpenAIService.generate(
prompt_ids=["product_description_v3"],
prompt_data=[{
"product_name": "Acme Widget",
"feature_list": ["A", "B", "C"],
}],
variables=[{"tone": "playful"}],
model="gpt-4",
)
Backed by SQL or MongoDB depending on database: in aibridge_config.yaml. Helpers in aibridgecore/prompts/.
For long jobs or fan-out workloads, enqueue the call and return a response ID immediately:
result = OpenAIService.generate(
prompts=["Long-running multi-step task..."],
model="gpt-4",
message_queue=True, # ← off by default
)
print(result) # {"response_id": "<uuid>"}
A worker (using aibridgecore.ai_services.process_mq) drains the Redis queue, executes the generate call, and stores the result for later retrieval. Configure Redis via redis_host / redis_port in the yaml.
from aibridgecore.ai_services.openai_images import OpenAIImage
result = OpenAIImage.generate(
prompts=["A cyberpunk samurai in Tokyo at night."],
model="dall-e-3",
size="1024x1024",
process_type="create", # "create", "variation", or "edit"
variation_count=1,
api_key="sk-...",
)
For process_type="variation" or "edit", also pass image_data=[...] (and mask_image=[...] for edits). Edits and variations require model="dall-e-2".
Stable Diffusion has the same shape — see aibridgecore.ai_services.stable_diffusion_image.StableDiffusion.
Provider-specific modules live in aibridgecore/video/. Same overall pattern as image generation — pass a prompt, get a generation response or queue ID back.
A single file — validators/provider_validator.py — holds:
BaseValidator with ~90% of the validation logicVALIDATORS registry mapping provider name → classBaseValidator
├── OpenAIValidator
├── KimiValidator (TEMP_MAX = 1.0)
├── AnthropicValidator
├── GeminiValidator
├── CohereValidator
├── GrokValidator
├── MistralValidator
├── DeepseekValidator
├── AlibabaValidator
├── OllamaValidator
└── Ai21Validator
from aibridgecore.validators.provider_validator import VALIDATORS
VALIDATORS["kimi"] # <class 'KimiValidator'>
VALIDATORS["openai"] # <class 'OpenAIValidator'>
BaseValidator checksPer-field methods on BaseValidator:
| Method | Validates |
|---|---|
validate_prompt_source(kwargs) | exactly one of prompts / prompt_ids is provided |
validate_prompts(prompts) | list of non-empty strings |
validate_prompt_ids(prompt_ids) | list |
validate_prompt_data(prompt_data) | list of dicts |
validate_variables(variables) | list |
validate_model(model) | required, must be a string |
validate_temperature(temperature) | numeric, TEMP_MIN <= temp <= TEMP_MAX |
validate_max_tokens(max_tokens) | positive integer |
validate_variation_count(variation_count) | positive integer |
validate_output_format(output_format, prompts) | list of "json" / "xml" / "csv" / "string", length matches prompts |
validate_format_structure(structure, output_format, prompts) | structured format string is parseable; length matches prompts |
validate_context(context) | list of {role, context} dicts; role is user / system / assistant |
validate_api_key(api_key) | non-empty string when provided |
class BaseValidator:
PROVIDER = "base"
ALLOWED_FORMATS = ["json", "xml", "csv", "string"]
ALLOWED_ROLES = ["user", "system", "assistant"]
TEMP_MIN = 0.0
TEMP_MAX = 2.0
Real overrides today:
class KimiValidator(BaseValidator):
PROVIDER = "kimi"
TEMP_MAX = 1.0
Every LLM service is wrapped on six entry points:
| Service method | validation_method classmethod |
|---|---|
generate | validate_generate |
generate_stream | validate_generate_stream |
execute_text_prompt | validate_execute_text_prompt_method |
execute_prompt_function_calling | validate_execute_prompt_function_calling_method |
get_prompt_context | validate_get_prompt_context_method |
get_response | validate_get_response_method |
e.code | Meaning |
|---|---|
MISSING_REQUIRED_FIELD | A required arg was None or absent |
EMPTY_VALUE | A required string is empty / whitespace |
INVALID_TYPE | Argument was of the wrong Python type |
INVALID_VALUE | Value didn't match an enum (output_format, role, etc.) |
OUT_OF_RANGE | Numeric value outside allowed range |
CONFLICTING_FIELDS | Mutually exclusive args both provided |
LENGTH_MISMATCH | List lengths don't match (e.g. output_format vs prompts) |
Exception
└── AIBridgeException (.code, .status_code, .message, .details, to_dict(), __str__, __repr__)
│
├── BadRequestException (HTTP 400 — generic bad request)
│ └── (ValidationException can be made to inherit — currently sibling)
├── AuthenticationException (HTTP 401)
├── TimeoutException (HTTP 408)
├── RateLimitException (HTTP 429)
├── ServerException (HTTP 5xx)
├── ValidationException (HTTP 400 — client-side validator caught it)
│
└── Provider-specific (internal layer, rarely surfaced):
OpenAIException, KimiException, AnthropicsException, GeminiException,
CohereException, Ai21Exception, GrokException, DeepseekException,
MistralException, AlibabaException, OllamaException
| Class | e.status_code |
|---|---|
ValidationException / BadRequestException | 400 |
AuthenticationException | 401, 403 |
TimeoutException | 408 |
RateLimitException | 429 |
ServerException | 500–599 (preserves upstream) |
e.code vs e.status_codeThese are two different things, matching the OpenAI SDK:
e.status_code — int HTTP status (401, 429, 500, ...).e.code — str machine-readable error code ("rate_limit_exceeded", "invalid_api_key", ...) or None when unknown.e.status_code # int — HTTP status (400, 401, 403, 429, ...)
e.code # str / None — semantic error code ("rate_limit_exceeded", ...)
e.category # str — error category (rate_limit_error, authentication_error, ...)
e.message # str — raw machine-friendly message
e.user_message # str — human-friendly version
e.param # str / None — which input parameter, if applicable
e.expected # str / None — what was expected
e.received # any / None — what was received
e.provider # str / None — which provider triggered
e.retryable # bool — whether retrying may succeed
e.developer_message # str — pre-formatted developer log line
e.details # dict — all of the above as a flat dict (always present)
e.to_dict() # method — returns e.details
AiBridgeValidationException is an alias of BadRequestException.
@provider_wrapper)Every public method on every service is decorated with @provider_wrapper(validator=..., validation_method=...). The wrapper:
variation_count=1 validates as 1, not None). Mutable defaults (prompts=[], context=[]) are copied per call so state never leaks between invocations.BadRequestException; wrapper sees it's a category class and lets it propagate.generate_stream) are detected automatically and their iteration is wrapped too, so errors raised mid-stream are caught the same way.e.details filled in to the full normalized shape); anything else gets normalized and transformed into the right category class.Because the wrapper covers every method shape — sync, generator, validation-time and mid-stream errors — service methods do not need their own try/except. It is the single error-handling layer.
_class_for_code() mapping400 → BadRequestException
401, 403 → AuthenticationException
408 → TimeoutException
429 → RateLimitException
500-599 → ServerException
other → BadRequestException
The wrapper reads PROVIDER from the validator class and stamps it onto e.details["provider"].
ErrorNormalizer.normalize(e, provider=None) inspects any exception and returns a
structured details dict. Normalization is status-code-driven: _GENERIC_BY_STATUS
covers the 12 HTTP status codes below, and a small ERROR_CATALOG holds only the
handful of provider-specific errors that need a non-generic mapping.
| Step | Trigger | Result |
|---|---|---|
| 1 | isinstance(e, ValidationException / BadRequestException) | validation_error / 400, passes param details through |
| 2 | provider known and e.code matches a catalog entry | provider-specific entry |
| 3 | a provider catalog match_keywords entry is found in the message | provider-specific entry |
| 4 | e.status_code is set | _GENERIC_BY_STATUS entry for that status |
| 5 | nothing matched | internal_server_error / 500 |
400 invalid request · 401 authentication · 402 insufficient balance ·
403 permission / region blocked · 404 not found · 408 timeout ·
422 unprocessable entity · 429 rate limit · 500 server error ·
502 bad gateway · 503 service unavailable / overloaded · 529 overloaded (Anthropic).
ERROR_CATALOG — provider-specific overrides only (7)Common errors (invalid_api_key, rate_limit_exceeded, model_not_found,
internal_server_error, ...) are not catalogued — they resolve straight from
_GENERIC_BY_STATUS by HTTP status. The catalog holds only entries whose behavior
differs from the generic mapping:
| Provider | Code | Why it overrides the generic |
|---|---|---|
| openai | insufficient_quota | 429 but retryable: False (billing, not rate) |
| openai | context_length_exceeded | 400 with param: messages + specific guidance |
| openai | engine_overloaded | 503 with provider-specific wording |
| anthropic | overloaded_error | non-standard 529 |
| gemini | safety_blocked | content-filter — no generic equivalent |
| ollama | out_of_memory | 500 but retryable: False |
| ollama | connection_refused | local-daemon reachability |
authentication_error · rate_limit_error · validation_error ·
provider_error · server_error · unknown_error.
ErrorNormalizer.status_codes() returns the sorted list of covered statuses;
ErrorNormalizer.lookup(provider, code) returns a single catalog entry.
@provider_wrapper runs the validator._raise() constructs BadRequestException with full details.details keys.BadRequestException. No HTTPS round-trip.openai.RateLimitError.try/except needed).status_code / code / "rate limit" → 429.RateLimitException, attaches full details, raises from e.RateLimitException.A service body may raise a typed exception directly:
raise OpenAIException("No prompts provided")
It propagates to the wrapper, which normalizes it into the right category class and fills in the full details. Provider methods do not need their own try/except — the wrapper is the single error layer.
The single most important section for SDK users.
from aibridgecore.exceptions import (
AIBridgeException,
ValidationException,
BadRequestException,
AuthenticationException,
RateLimitException,
TimeoutException,
ServerException,
)
try:
response = OpenAIService.generate(...)
except ValidationException as e:
pass
except AuthenticationException as e:
pass
except RateLimitException as e:
pass
except TimeoutException as e:
pass
except ServerException as e:
pass
except BadRequestException as e:
pass
except AIBridgeException as e:
pass
Catch most-specific first.
try:
response = OpenAIService.generate(...)
except AIBridgeException as e:
log.error(e.developer_message)
return {"error": e.to_dict()}, e.status_code
except AIBridgeException as e:
e.status_code # int — 400, 401, 403, 408, 429, 5xx
e.code # str / None — semantic error code ("rate_limit_exceeded")
e.category # str — error category
e.message
e.user_message
e.param
e.expected
e.received
e.provider
e.retryable
e.developer_message
e.details # flat dict (always present)
e.to_dict() # returns e.details
Pre-validation — temperature out of range:
try:
OpenAIService.generate(prompts=["hi"], variation_count=1, model="gpt-4", temperature=99)
except BadRequestException as e:
e.code # OUT_OF_RANGE (str — semantic error code)
e.status_code # 400 (int — HTTP status)
e.param # temperature
e.expected # 0.0 <= temperature <= 2.0
e.received # 99
e.user_message # Please use a temperature between 0.0 and 2.0.
Uncaught traceback:
aibridgecore.exceptions.BadRequestException: {'error': {'code': 'OUT_OF_RANGE',
'status_code': 400, 'category': 'validation_error',
'message': 'temperature must be between 0.0 and 2.0',
'user_message': 'Please use a temperature between 0.0 and 2.0.',
'param': 'temperature', 'expected': '0.0 <= temperature <= 2.0',
'received': 99, 'provider': 'openai',
'developer_message': '[openai] [OUT_OF_RANGE] param=temperature ...'}}
Provider rate limit:
except RateLimitException as e:
e.code # rate_limit_exceeded (str — semantic error code)
e.status_code # 429 (int — HTTP status)
e.category # rate_limit_error
e.retryable # True
e.user_message # Too many requests. Please slow down and try again shortly.
e.provider # openai
Provider auth failure:
except AuthenticationException as e:
e.code # invalid_api_key (str — semantic error code)
e.status_code # 401 (int — HTTP status)
e.category # authentication_error
e.user_message # Authentication failed. Please verify your API key.
import json
try:
OpenAIService.generate(...)
except AIBridgeException as e:
json.dumps(e.to_dict()) # safe over the wire
log.error(e.developer_message) # pre-formatted log line
import time
from aibridgecore.exceptions import (
AIBridgeException, RateLimitException, TimeoutException, ServerException,
)
def call_with_retry(svc, **kwargs):
delay = 1
for attempt in range(5):
try:
return svc.generate(**kwargs)
except (RateLimitException, TimeoutException, ServerException):
time.sleep(delay)
delay *= 2
except AIBridgeException:
raise
raise
| Class | When you'll see it |
|---|---|
ValidationException | Bad input. Our validator caught it before any network call. Fix the input. |
BadRequestException | Provider returned 400. Inspect e.message. |
AuthenticationException | Provider returned 401. API key bad / missing / revoked. |
TimeoutException | Provider returned 408 or the request timed out. Retry. |
RateLimitException | Provider returned 429. Backoff and retry. |
ServerException | Provider returned 5xx. Retry or fall back. |
| Provider | Service class | Validator | File |
|---|---|---|---|
| OpenAI | OpenAIService | OpenAIValidator | ai_services/openai_services.py |
| Anthropic (Claude) | AnthropicService | AnthropicValidator | ai_services/anthropic_ai.py |
| Google Gemini | GeminiAIService | GeminiValidator | ai_services/geminin_services.py |
| Cohere | CohereApi | CohereValidator | ai_services/cohere_llm.py |
| Grok | GrokService | GrokValidator | ai_services/grok_services.py |
| Mistral | MistralService | MistralValidator | ai_services/mistral_services.py |
| Deepseek | DeepseekService | DeepseekValidator | ai_services/deepseek_services.py |
| Kimi (Moonshot) | KimiService | KimiValidator (TEMP_MAX=1.0) | ai_services/kimi_services.py |
| Alibaba (Qwen) | AlibabaService | AlibabaValidator | ai_services/alibaba_services.py |
| AI21 | AI21labsText | Ai21Validator | ai_services/ai21labs_text.py |
| Ollama (self-hosted) | OllamaService | OllamaValidator | ai_services/ollama_services.py |
| OpenAI Images (DALL·E) | OpenAIImage | not yet | ai_services/openai_images.py |
| Stable Diffusion | StableDiffusion | not yet | ai_services/stable_diffusion_image.py |
| File | generate | stream | exec_txt | exec_fn | prompt_ctx | get_resp |
|---|---|---|---|---|---|---|
| openai_services | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| kimi_services | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| anthropic_ai | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| geminin_services | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| cohere_llm | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| grok_services | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| mistral_services | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| deepseek_services | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| alibaba_services | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| ollama_services | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| ai21labs_text | ✓ | ✓ | — | — | ✓ | ✓ |
Four steps.
BaseValidatorclass NewProviderValidator(BaseValidator):
PROVIDER = "newprovider"
TEMP_MAX = 1.5
VALIDATORS = {
...
"newprovider": NewProviderValidator,
}
from aibridgecore.validators.provider_validator import NewProviderValidator
from aibridgecore.wrappers.provider_wrapper import provider_wrapper
from aibridgecore.exceptions import AIBridgeException, NewProviderException
class NewProviderService(AIInterface):
@classmethod
@provider_wrapper(
validator=NewProviderValidator,
validation_method="validate_generate",
)
def generate(self, prompts=[], model="np-default", variation_count=1, ...):
try:
...
except AIBridgeException:
raise
except Exception as e:
raise NewProviderException(e)
Repeat for generate_stream, execute_text_prompt, execute_prompt_function_calling, get_prompt_context, get_response.
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from aibridgecore.ai_services.openai_services import OpenAIService
from aibridgecore.exceptions import AIBridgeException
app = FastAPI()
@app.exception_handler(AIBridgeException)
def aibridge_handler(request, exc: AIBridgeException):
return JSONResponse(status_code=exc.status_code, content={"error": exc.to_dict()})
@app.post("/generate")
def generate(payload: dict):
return OpenAIService.generate(**payload)
from flask import Flask, jsonify, request
from aibridgecore.ai_services.openai_services import OpenAIService
from aibridgecore.exceptions import AIBridgeException
app = Flask(__name__)
@app.errorhandler(AIBridgeException)
def handle_aibridge(exc):
return jsonify({"error": exc.to_dict()}), exc.status_code
@app.post("/generate")
def generate():
return jsonify(OpenAIService.generate(**request.json))
A 429 from a provider, a 401 for a bad key, or a 400 from the validator all flow into the same handler.
python -m pytest tests/test_detailed_errors.py -v
The test file loads modules via importlib.util to exercise validator + normalizer + exception layers without importing every provider SDK.
from aibridgecore.validators.provider_validator import OpenAIValidator
from aibridgecore.exceptions import BadRequestException
try:
OpenAIValidator.validate_generate({
"prompts": ["hello"],
"variation_count": 1,
"model": "gpt-4",
"temperature": 99,
})
except BadRequestException as e:
assert e.code == "OUT_OF_RANGE"
assert e.param == "temperature"
aibridgecore/
├── ai_services/
│ ├── openai_services.py OpenAIService
│ ├── kimi_services.py KimiService
│ ├── anthropic_ai.py AnthropicService
│ ├── geminin_services.py GeminiAIService
│ ├── cohere_llm.py CohereApi
│ ├── grok_services.py GrokService
│ ├── mistral_services.py MistralService
│ ├── deepseek_services.py DeepseekService
│ ├── alibaba_services.py AlibabaService
│ ├── ai21labs_text.py AI21labsText
│ ├── ollama_services.py OllamaService
│ ├── openai_images.py OpenAIImage (DALL·E)
│ ├── stable_diffusion_image.py StableDiffusion
│ ├── image_optimisaton.py (internal helper)
│ ├── ai_services_response.py response store
│ ├── process_mq.py Redis queue worker
│ └── ai_abstraction.py AIInterface base contract
│
├── validators/
│ └── provider_validator.py BaseValidator + subclasses + VALIDATORS
│
├── wrappers/
│ └── provider_wrapper.py @provider_wrapper + _class_for_code
│
├── error_hanldling/
│ └── error_normalizer.py ErrorNormalizer + inline ERROR_CATALOG
│
├── output_validation/ JSON / XML / CSV parsers
├── prompts/ stored prompts + variables
├── queue_integration/ Redis integration
├── database/ SQL / MongoDB layer
├── constant/ provider URLs, allowed sizes, etc.
├── image/ image utilities
├── video/ video generation modules
├── exceptions.py exception hierarchy
└── setconfig.py config loader
ValidationException, BadRequestException, AuthenticationException, TimeoutException, RateLimitException, ServerException. What callers except against.OpenAIException, KimiException. The wrapper transforms these into category classes._raise() or _build_error() and attached as e.details. Always present.e.code) — semantic string like OUT_OF_RANGE, rate_limit_exceeded, invalid_api_key. Independent of HTTP status.e.status_code) — numeric HTTP status (400, 401, 403, 429, ...). Fixed per category class except ServerException which preserves the upstream value.e.category) — one of authentication_error, rate_limit_error, validation_error, provider_error, server_error, unknown_error.[openai] [OUT_OF_RANGE] param=temperature .... Render user_message to humans instead._CATEGORY_CLASSES — tuple in provider_wrapper.py of category classes. The wrapper checks isinstance(e, _CATEGORY_CLASSES) to decide whether to transform or pass through.See LICENSE.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 1.5.24 | 4 Sept 2026 | 15 |
This describes our collected data; it is not a security assessment.
Observed since 21 Oct, 10:03 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:d35f434fe6c3dfcca31f948d673987a2229802ca92c493da627b4e844aa43f3b