studiorelay 0.3.0__py3-none-any.whl

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.
Files changed (39) hide show
  1. kimibridge/__init__.py +1 -0
  2. kimibridge/cli.py +461 -0
  3. kimibridge/compatibility/__init__.py +25 -0
  4. kimibridge/compatibility/errors.py +62 -0
  5. kimibridge/compatibility/model_aliases.py +44 -0
  6. kimibridge/compatibility/models.py +69 -0
  7. kimibridge/compatibility/parameters.py +29 -0
  8. kimibridge/compatibility/pipeline.py +42 -0
  9. kimibridge/compatibility/profiles.py +138 -0
  10. kimibridge/compatibility/responses.py +766 -0
  11. kimibridge/compatibility/roles.py +33 -0
  12. kimibridge/config.py +195 -0
  13. kimibridge/health.py +51 -0
  14. kimibridge/ports.py +255 -0
  15. kimibridge/server.py +309 -0
  16. kimibridge/service.py +182 -0
  17. kimibridge/upstream.py +168 -0
  18. studiorelay/__init__.py +1 -0
  19. studiorelay/cli.py +514 -0
  20. studiorelay/compatibility/__init__.py +25 -0
  21. studiorelay/compatibility/errors.py +62 -0
  22. studiorelay/compatibility/model_aliases.py +44 -0
  23. studiorelay/compatibility/models.py +69 -0
  24. studiorelay/compatibility/parameters.py +29 -0
  25. studiorelay/compatibility/pipeline.py +42 -0
  26. studiorelay/compatibility/profiles.py +138 -0
  27. studiorelay/compatibility/responses.py +766 -0
  28. studiorelay/compatibility/roles.py +33 -0
  29. studiorelay/config.py +326 -0
  30. studiorelay/health.py +53 -0
  31. studiorelay/ports.py +277 -0
  32. studiorelay/server.py +321 -0
  33. studiorelay/service.py +237 -0
  34. studiorelay/upstream.py +171 -0
  35. studiorelay-0.3.0.dist-info/METADATA +395 -0
  36. studiorelay-0.3.0.dist-info/RECORD +39 -0
  37. studiorelay-0.3.0.dist-info/WHEEL +5 -0
  38. studiorelay-0.3.0.dist-info/entry_points.txt +4 -0
  39. studiorelay-0.3.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from copy import deepcopy
4
+ from typing import Any
5
+
6
+ from kimibridge.compatibility.profiles import KIMI_PROFILE, ProviderProfile
7
+
8
+ UNSUPPORTED_PARAMETERS = {
9
+ "metadata",
10
+ "parallel_tool_calls",
11
+ "store",
12
+ }
13
+
14
+
15
+ def normalize_parameters(
16
+ payload: dict[str, Any],
17
+ mode: str = "compatible",
18
+ profile: ProviderProfile | None = None,
19
+ ) -> dict[str, Any]:
20
+ if mode in {"passthrough", "strict"}:
21
+ return deepcopy(payload)
22
+
23
+ active_profile = profile or KIMI_PROFILE
24
+ keys_to_remove = active_profile.unsupported_parameters if active_profile else UNSUPPORTED_PARAMETERS
25
+
26
+ normalized = deepcopy(payload)
27
+ for key in keys_to_remove:
28
+ normalized.pop(key, None)
29
+ return normalized
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from kimibridge.compatibility.model_aliases import normalize_model_aliases
6
+ from kimibridge.compatibility.parameters import normalize_parameters
7
+ from kimibridge.compatibility.profiles import (
8
+ ProviderProfile,
9
+ detect_provider,
10
+ get_provider_profile,
11
+ )
12
+ from kimibridge.compatibility.roles import normalize_roles
13
+
14
+
15
+ VALID_MODES = {"compatible", "passthrough", "strict"}
16
+
17
+
18
+ def normalize_request(
19
+ payload: dict[str, Any],
20
+ mode: str = "compatible",
21
+ provider: str | ProviderProfile | None = None,
22
+ base_url: str | None = None,
23
+ headers: dict[str, str] | None = None,
24
+ ) -> dict[str, Any]:
25
+ if mode not in VALID_MODES:
26
+ raise ValueError(f"Unsupported compatibility mode: {mode}")
27
+
28
+ if isinstance(provider, ProviderProfile):
29
+ profile = provider
30
+ else:
31
+ profile = detect_provider(
32
+ model=payload.get("model") if isinstance(payload, dict) else None,
33
+ base_url=base_url,
34
+ configured_provider=provider,
35
+ headers=headers,
36
+ )
37
+
38
+ normalized = normalize_roles(payload, mode=mode, profile=profile)
39
+ normalized = normalize_parameters(normalized, mode=mode, profile=profile)
40
+ if mode != "passthrough":
41
+ normalized = normalize_model_aliases(normalized, profile=profile)
42
+ return normalized
@@ -0,0 +1,138 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class ProviderProfile:
9
+ name: str
10
+ supports_developer_role: bool = False
11
+ developer_role_target: str = "system"
12
+ supported_roles: frozenset[str] = frozenset({"system", "user", "assistant", "tool"})
13
+ unsupported_parameters: frozenset[str] = frozenset()
14
+ default_base_url: str = ""
15
+ default_models: tuple[dict[str, Any], ...] = ()
16
+
17
+
18
+ KIMI_PROFILE = ProviderProfile(
19
+ name="kimi",
20
+ supports_developer_role=False,
21
+ developer_role_target="system",
22
+ supported_roles=frozenset({"system", "user", "assistant", "tool"}),
23
+ unsupported_parameters=frozenset({"metadata", "parallel_tool_calls", "store"}),
24
+ default_base_url="https://api.moonshot.ai",
25
+ default_models=(
26
+ {"id": "kimi-k3", "object": "model", "owned_by": "moonshot"},
27
+ {"id": "kimi-latest", "object": "model", "owned_by": "moonshot"},
28
+ {"id": "kimi-k1.5", "object": "model", "owned_by": "moonshot"},
29
+ {"id": "kimi-k2", "object": "model", "owned_by": "moonshot"},
30
+ {"id": "moonshot-v1-auto", "object": "model", "owned_by": "moonshot"},
31
+ {"id": "moonshot-v1-8k", "object": "model", "owned_by": "moonshot"},
32
+ {"id": "moonshot-v1-32k", "object": "model", "owned_by": "moonshot"},
33
+ {"id": "moonshot-v1-128k", "object": "model", "owned_by": "moonshot"},
34
+ {"id": "moonshot-v1-8k-vision-preview", "object": "model", "owned_by": "moonshot"},
35
+ {"id": "moonshot-v1-32k-vision-preview", "object": "model", "owned_by": "moonshot"},
36
+ {"id": "moonshot-v1-128k-vision-preview", "object": "model", "owned_by": "moonshot"},
37
+ ),
38
+ )
39
+
40
+ DEEPSEEK_PROFILE = ProviderProfile(
41
+ name="deepseek",
42
+ supports_developer_role=False,
43
+ developer_role_target="system",
44
+ supported_roles=frozenset({"system", "user", "assistant", "tool", "latest_reminder"}),
45
+ unsupported_parameters=frozenset({"metadata", "store"}),
46
+ default_base_url="https://api.deepseek.com",
47
+ default_models=(
48
+ {"id": "deepseek-flash", "object": "model", "owned_by": "deepseek"},
49
+ {"id": "deepseek-v4-pro", "object": "model", "owned_by": "deepseek"},
50
+ {"id": "deepseek-chat", "object": "model", "owned_by": "deepseek"},
51
+ {"id": "deepseek-reasoner", "object": "model", "owned_by": "deepseek"},
52
+ {"id": "deepseek-v3", "object": "model", "owned_by": "deepseek"},
53
+ {"id": "deepseek-v3-pro", "object": "model", "owned_by": "deepseek"},
54
+ {"id": "deepseek-r1", "object": "model", "owned_by": "deepseek"},
55
+ {"id": "deepseek-coder", "object": "model", "owned_by": "deepseek"},
56
+ ),
57
+ )
58
+
59
+ OPENAI_PROFILE = ProviderProfile(
60
+ name="openai",
61
+ supports_developer_role=True,
62
+ developer_role_target="developer",
63
+ supported_roles=frozenset({"system", "developer", "user", "assistant", "tool", "function"}),
64
+ unsupported_parameters=frozenset(),
65
+ default_base_url="https://api.openai.com",
66
+ default_models=(
67
+ {"id": "gpt-4o", "object": "model", "owned_by": "openai"},
68
+ {"id": "gpt-4o-mini", "object": "model", "owned_by": "openai"},
69
+ {"id": "o1", "object": "model", "owned_by": "openai"},
70
+ {"id": "o3-mini", "object": "model", "owned_by": "openai"},
71
+ ),
72
+ )
73
+
74
+ GENERIC_PROFILE = ProviderProfile(
75
+ name="generic",
76
+ supports_developer_role=False,
77
+ developer_role_target="system",
78
+ supported_roles=frozenset({"system", "user", "assistant", "tool"}),
79
+ unsupported_parameters=frozenset({"metadata", "parallel_tool_calls", "store"}),
80
+ default_base_url="",
81
+ default_models=(
82
+ {"id": "default-model", "object": "model", "owned_by": "generic"},
83
+ ),
84
+ )
85
+
86
+ PROFILES: dict[str, ProviderProfile] = {
87
+ "kimi": KIMI_PROFILE,
88
+ "moonshot": KIMI_PROFILE,
89
+ "deepseek": DEEPSEEK_PROFILE,
90
+ "openai": OPENAI_PROFILE,
91
+ "generic": GENERIC_PROFILE,
92
+ }
93
+
94
+
95
+ def get_provider_profile(name_or_alias: str | None) -> ProviderProfile:
96
+ if not name_or_alias:
97
+ return KIMI_PROFILE
98
+ key = name_or_alias.strip().lower()
99
+ return PROFILES.get(key, GENERIC_PROFILE)
100
+
101
+
102
+ def detect_provider(
103
+ model: str | None = None,
104
+ base_url: str | None = None,
105
+ configured_provider: str | None = None,
106
+ headers: dict[str, str] | None = None,
107
+ ) -> ProviderProfile:
108
+ """
109
+ Detect provider profile based on explicit headers, upstream base URL heuristic,
110
+ model name heuristic, or configuration.
111
+ """
112
+ if headers:
113
+ for k, v in headers.items():
114
+ if k.lower() in {"x-bridge-provider", "x-provider"}:
115
+ return get_provider_profile(v)
116
+
117
+ if model:
118
+ m = model.lower()
119
+ if "deepseek" in m:
120
+ return DEEPSEEK_PROFILE
121
+ if "kimi" in m or "moonshot" in m:
122
+ return KIMI_PROFILE
123
+ if m.startswith("gpt-") or m.startswith("o1") or m.startswith("o3"):
124
+ return OPENAI_PROFILE
125
+
126
+ if base_url:
127
+ u = base_url.lower()
128
+ if "deepseek.com" in u:
129
+ return DEEPSEEK_PROFILE
130
+ if "moonshot.ai" in u:
131
+ return KIMI_PROFILE
132
+ if "openai.com" in u:
133
+ return OPENAI_PROFILE
134
+
135
+ if configured_provider and configured_provider.lower() not in {"auto", "default", "all"}:
136
+ return get_provider_profile(configured_provider)
137
+
138
+ return KIMI_PROFILE