argo-proxy 3.2.2__tar.gz → 3.2.3__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.
Files changed (52) hide show
  1. {argo_proxy-3.2.2/src/argo_proxy.egg-info → argo_proxy-3.2.3}/PKG-INFO +2 -2
  2. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/pyproject.toml +1 -1
  3. {argo_proxy-3.2.2 → argo_proxy-3.2.3/src/argo_proxy.egg-info}/PKG-INFO +2 -2
  4. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argo_proxy.egg-info/SOURCES.txt +4 -2
  5. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argo_proxy.egg-info/requires.txt +1 -1
  6. argo_proxy-3.2.3/src/argoproxy/__init__.py +1 -0
  7. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/cli/handlers.py +3 -3
  8. argo_proxy-3.2.3/src/argoproxy/models/__init__.py +45 -0
  9. argo_proxy-3.2.3/src/argoproxy/models/constants.py +153 -0
  10. argo_proxy-3.2.3/src/argoproxy/models/registry.py +432 -0
  11. argo_proxy-3.2.3/src/argoproxy/models/upstream.py +343 -0
  12. argo_proxy-3.2.2/src/argoproxy/__init__.py +0 -1
  13. argo_proxy-3.2.2/src/argoproxy/models.py +0 -1013
  14. argo_proxy-3.2.2/src/argoproxy/utils/models.py +0 -23
  15. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/LICENSE +0 -0
  16. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/README.md +0 -0
  17. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/setup.cfg +0 -0
  18. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argo_proxy.egg-info/dependency_links.txt +0 -0
  19. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argo_proxy.egg-info/entry_points.txt +0 -0
  20. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argo_proxy.egg-info/top_level.txt +0 -0
  21. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/_vendor/__init__.py +0 -0
  22. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/_vendor/semver.py +0 -0
  23. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/_vendor/yaml.py +0 -0
  24. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/app.py +0 -0
  25. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/cli/__init__.py +0 -0
  26. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/cli/display.py +0 -0
  27. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/cli/parser.py +0 -0
  28. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/config/__init__.py +0 -0
  29. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/config/interactive.py +0 -0
  30. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/config/io.py +0 -0
  31. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/config/model.py +0 -0
  32. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/config/validation.py +0 -0
  33. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/endpoints/dev_proxy.py +0 -0
  34. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/endpoints/dispatch.py +0 -0
  35. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/endpoints/extras.py +0 -0
  36. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/endpoints/passthrough.py +0 -0
  37. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/performance.py +0 -0
  38. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/py.typed +0 -0
  39. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/utils/attack_logger.py +0 -0
  40. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/utils/image_processing.py +0 -0
  41. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/utils/logging.py +0 -0
  42. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/utils/misc.py +0 -0
  43. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/src/argoproxy/utils/transports.py +0 -0
  44. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/test/test_chat_completions.py +0 -0
  45. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/test/test_embeddings.py +0 -0
  46. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/test/test_function_calling_multiple.py +0 -0
  47. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/test/test_function_calling_single.py +0 -0
  48. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/test/test_leaked_tool_parser.py +0 -0
  49. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/test/test_model_resolution.py +0 -0
  50. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/tests/test_config_migrate.py +0 -0
  51. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/tests/test_security_middleware.py +0 -0
  52. {argo_proxy-3.2.2 → argo_proxy-3.2.3}/tests/test_unix_socket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: argo-proxy
3
- Version: 3.2.2
3
+ Version: 3.2.3
4
4
  Summary: Proxy server to Argo API, OpenAI format compatible
5
5
  Author-email: Peng Ding <oaklight@gmx.com>
6
6
  License-Expression: MIT
@@ -15,7 +15,7 @@ Requires-Python: >=3.10
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: aiohttp>=3.12.2
18
- Requires-Dist: llm-rosetta>=0.7.0
18
+ Requires-Dist: llm-rosetta>=0.7.1
19
19
  Requires-Dist: pydantic>=2.11.7
20
20
  Requires-Dist: tiktoken>=0.9.0
21
21
  Requires-Dist: tqdm>=4.67.1
@@ -20,7 +20,7 @@ classifiers = [
20
20
 
21
21
  dependencies = [
22
22
  "aiohttp>=3.12.2",
23
- "llm-rosetta>=0.7.0",
23
+ "llm-rosetta>=0.7.1",
24
24
  "pydantic>=2.11.7",
25
25
  "tiktoken>=0.9.0",
26
26
  "tqdm>=4.67.1",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: argo-proxy
3
- Version: 3.2.2
3
+ Version: 3.2.3
4
4
  Summary: Proxy server to Argo API, OpenAI format compatible
5
5
  Author-email: Peng Ding <oaklight@gmx.com>
6
6
  License-Expression: MIT
@@ -15,7 +15,7 @@ Requires-Python: >=3.10
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: aiohttp>=3.12.2
18
- Requires-Dist: llm-rosetta>=0.7.0
18
+ Requires-Dist: llm-rosetta>=0.7.1
19
19
  Requires-Dist: pydantic>=2.11.7
20
20
  Requires-Dist: tiktoken>=0.9.0
21
21
  Requires-Dist: tqdm>=4.67.1
@@ -9,7 +9,6 @@ src/argo_proxy.egg-info/requires.txt
9
9
  src/argo_proxy.egg-info/top_level.txt
10
10
  src/argoproxy/__init__.py
11
11
  src/argoproxy/app.py
12
- src/argoproxy/models.py
13
12
  src/argoproxy/performance.py
14
13
  src/argoproxy/py.typed
15
14
  src/argoproxy/_vendor/__init__.py
@@ -28,11 +27,14 @@ src/argoproxy/endpoints/dev_proxy.py
28
27
  src/argoproxy/endpoints/dispatch.py
29
28
  src/argoproxy/endpoints/extras.py
30
29
  src/argoproxy/endpoints/passthrough.py
30
+ src/argoproxy/models/__init__.py
31
+ src/argoproxy/models/constants.py
32
+ src/argoproxy/models/registry.py
33
+ src/argoproxy/models/upstream.py
31
34
  src/argoproxy/utils/attack_logger.py
32
35
  src/argoproxy/utils/image_processing.py
33
36
  src/argoproxy/utils/logging.py
34
37
  src/argoproxy/utils/misc.py
35
- src/argoproxy/utils/models.py
36
38
  src/argoproxy/utils/transports.py
37
39
  test/test_chat_completions.py
38
40
  test/test_embeddings.py
@@ -1,5 +1,5 @@
1
1
  aiohttp>=3.12.2
2
- llm-rosetta>=0.7.0
2
+ llm-rosetta>=0.7.1
3
3
  pydantic>=2.11.7
4
4
  tiktoken>=0.9.0
5
5
  tqdm>=4.67.1
@@ -0,0 +1 @@
1
+ __version__ = "3.2.3"
@@ -700,7 +700,7 @@ def handle_models(args: argparse.Namespace):
700
700
  from collections import defaultdict
701
701
 
702
702
  from ..config import load_config
703
- from ..models import ModelRegistry
703
+ from ..models import ModelRegistry, classify_model_family
704
704
 
705
705
  config_data, _ = load_config(args.config, verbose=False)
706
706
  if not config_data:
@@ -762,7 +762,7 @@ def handle_models(args: argparse.Namespace):
762
762
  if args.json:
763
763
  output = []
764
764
  for internal_id, aliases in sorted(chat_id_to_aliases.items()):
765
- family = registry._classify_model_by_family(internal_id)
765
+ family = classify_model_family(internal_id)
766
766
  output.append(
767
767
  {
768
768
  "upstream_id": internal_id,
@@ -807,7 +807,7 @@ def handle_models(args: argparse.Namespace):
807
807
  # Classify chat models by family
808
808
  chat_families: dict[str, list[tuple[str, list[str]]]] = defaultdict(list)
809
809
  for internal_id, aliases in sorted(chat_id_to_aliases.items()):
810
- family = registry._classify_model_by_family(internal_id)
810
+ family = classify_model_family(internal_id)
811
811
  chat_families[family].append((internal_id, aliases))
812
812
 
813
813
  for family in family_order:
@@ -0,0 +1,45 @@
1
+ """Model registry, classification, and upstream communication."""
2
+
3
+ from .constants import (
4
+ ANTHROPIC_PATTERNS,
5
+ GEMINI_PATTERN,
6
+ GPT_O_PATTERN,
7
+ NATIVE_TOOL_CALL_MODELS,
8
+ NATIVE_TOOL_CALL_PATTERNS,
9
+ NO_SYS_MSG_MODELS,
10
+ NO_SYS_MSG_PATTERNS,
11
+ OPTION_2_INPUT_MODELS,
12
+ OPTION_2_INPUT_PATTERNS,
13
+ TIKTOKEN_ENCODING_PREFIX_MAPPING,
14
+ _DEFAULT_CHAT_MODELS,
15
+ _EMBED_MODELS,
16
+ classify_model_family,
17
+ filter_model_by_patterns,
18
+ flatten_mapping,
19
+ is_anthropic_model,
20
+ )
21
+ from .registry import ModelRegistry, OpenAIModel
22
+ from .upstream import Model, produce_argo_model_list
23
+
24
+ __all__ = [
25
+ "ANTHROPIC_PATTERNS",
26
+ "GEMINI_PATTERN",
27
+ "GPT_O_PATTERN",
28
+ "NATIVE_TOOL_CALL_MODELS",
29
+ "NATIVE_TOOL_CALL_PATTERNS",
30
+ "NO_SYS_MSG_MODELS",
31
+ "NO_SYS_MSG_PATTERNS",
32
+ "OPTION_2_INPUT_MODELS",
33
+ "OPTION_2_INPUT_PATTERNS",
34
+ "TIKTOKEN_ENCODING_PREFIX_MAPPING",
35
+ "Model",
36
+ "ModelRegistry",
37
+ "OpenAIModel",
38
+ "_DEFAULT_CHAT_MODELS",
39
+ "_EMBED_MODELS",
40
+ "classify_model_family",
41
+ "filter_model_by_patterns",
42
+ "flatten_mapping",
43
+ "is_anthropic_model",
44
+ "produce_argo_model_list",
45
+ ]
@@ -0,0 +1,153 @@
1
+ """Model constants, default lists, patterns, and classification helpers."""
2
+
3
+ import fnmatch
4
+ from typing import Any
5
+
6
+
7
+ def flatten_mapping(mapping: dict[str, Any]) -> dict[str, str]:
8
+ flat = {}
9
+ for model, aliases in mapping.items():
10
+ if isinstance(aliases, str):
11
+ flat[aliases] = model
12
+ else:
13
+ for alias in aliases:
14
+ flat[alias] = model
15
+ return flat
16
+
17
+
18
+ # Default models fallback — kept in sync with the live /v1/models endpoint.
19
+ _DEFAULT_CHAT_MODELS = flatten_mapping(
20
+ {
21
+ # openai – gpt-4o (legacy, still served)
22
+ "gpt4o": "argo:gpt-4o",
23
+ # openai – o-series reasoning
24
+ "gpto1": ["argo:gpt-o1", "argo:o1"],
25
+ "gpto3mini": ["argo:gpt-o3-mini", "argo:o3-mini"],
26
+ "gpto3": ["argo:gpt-o3", "argo:o3"],
27
+ "gpto4mini": ["argo:gpt-o4-mini", "argo:o4-mini"],
28
+ # openai – gpt-4.1 family
29
+ "gpt41": "argo:gpt-4.1",
30
+ "gpt41mini": "argo:gpt-4.1-mini",
31
+ "gpt41nano": "argo:gpt-4.1-nano",
32
+ # openai – gpt-5 family
33
+ "gpt5": "argo:gpt-5",
34
+ "gpt5mini": "argo:gpt-5-mini",
35
+ "gpt5nano": "argo:gpt-5-nano",
36
+ "gpt51": "argo:gpt-5.1",
37
+ "gpt52": "argo:gpt-5.2",
38
+ "gpt54": "argo:gpt-5.4",
39
+ "gpt54mini": "argo:gpt-5.4-mini",
40
+ "gpt54nano": "argo:gpt-5.4-nano",
41
+ "gpt55": "argo:gpt-5.5",
42
+ # gemini
43
+ "gemini25pro": "argo:gemini-2.5-pro",
44
+ "gemini25flash": "argo:gemini-2.5-flash",
45
+ "gemini35flash": "argo:gemini-3.5-flash",
46
+ "gemini31flashlite": "argo:gemini-3.1-flash-lite",
47
+ # claude – opus
48
+ "claudeopus48": ["argo:claude-4.8-opus", "argo:claude-opus-4.8"],
49
+ "claudeopus47": ["argo:claude-4.7-opus", "argo:claude-opus-4.7"],
50
+ "claudeopus46": ["argo:claude-4.6-opus", "argo:claude-opus-4.6"],
51
+ "claudeopus45": ["argo:claude-4.5-opus", "argo:claude-opus-4.5"],
52
+ "claudeopus41": ["argo:claude-4.1-opus", "argo:claude-opus-4.1"],
53
+ # claude – sonnet
54
+ "claudesonnet5": ["argo:claude-5-sonnet", "argo:claude-sonnet-5"],
55
+ "claudesonnet46": ["argo:claude-4.6-sonnet", "argo:claude-sonnet-4.6"],
56
+ "claudesonnet45": ["argo:claude-4.5-sonnet", "argo:claude-sonnet-4.5"],
57
+ # claude – haiku
58
+ "claudehaiku45": ["argo:claude-4.5-haiku", "argo:claude-haiku-4.5"],
59
+ }
60
+ )
61
+
62
+ _EMBED_MODELS = flatten_mapping(
63
+ {
64
+ "ada002": "argo:text-embedding-ada-002",
65
+ "v3small": "argo:text-embedding-3-small",
66
+ "v3large": "argo:text-embedding-3-large",
67
+ }
68
+ )
69
+
70
+
71
+ def filter_model_by_patterns(
72
+ model_dict: dict[str, str], patterns: set[str]
73
+ ) -> list[str]:
74
+ """Filter model_dict values (model_id) by given fnmatch patterns,
75
+ returning both the model_name (key) and model_id (value) for matches."""
76
+ matching = set()
77
+ for model_name, model_id in model_dict.items():
78
+ if any(fnmatch.fnmatch(model_id, pattern) for pattern in patterns):
79
+ matching.add(model_name)
80
+ matching.add(model_id)
81
+ return sorted(matching)
82
+
83
+
84
+ # any models that unable to handle system prompt (o1-mini / o1-preview retired)
85
+ NO_SYS_MSG_PATTERNS: set[str] = set()
86
+
87
+ NO_SYS_MSG_MODELS = filter_model_by_patterns(
88
+ _DEFAULT_CHAT_MODELS,
89
+ NO_SYS_MSG_PATTERNS,
90
+ )
91
+
92
+
93
+ # any models that only able to handle single system prompt and no system prompt at all
94
+ OPTION_2_INPUT_PATTERNS: set[str] = set()
95
+
96
+ OPTION_2_INPUT_MODELS = filter_model_by_patterns(
97
+ _DEFAULT_CHAT_MODELS,
98
+ OPTION_2_INPUT_PATTERNS,
99
+ )
100
+
101
+ # any models that supports native tool call
102
+ NATIVE_TOOL_CALL_PATTERNS: set[str] = {
103
+ "*o1",
104
+ "*o3*",
105
+ "*o4*",
106
+ }
107
+
108
+ NATIVE_TOOL_CALL_MODELS = filter_model_by_patterns(
109
+ _DEFAULT_CHAT_MODELS,
110
+ NATIVE_TOOL_CALL_PATTERNS,
111
+ )
112
+
113
+ TIKTOKEN_ENCODING_PREFIX_MAPPING = {
114
+ "gpt5": "o200k_base", # gpt-5 family
115
+ "gpto": "o200k_base", # o-series
116
+ "gpt4o": "o200k_base", # gpt-4o
117
+ "gpt41": "o200k_base", # gpt-4.1 family
118
+ # this order need to be preserved to correctly parse mapping
119
+ "gpt4": "cl100k_base", # gpt-4 series (legacy fallback)
120
+ "ada002": "cl100k_base", # embedding
121
+ "v3": "cl100k_base", # embedding
122
+ }
123
+
124
+ # --- Provider detection patterns ---
125
+
126
+ GPT_O_PATTERN = "gpto*"
127
+ GEMINI_PATTERN = "gemini*"
128
+
129
+ ANTHROPIC_PATTERNS = ("claude*", "sonnet*", "opus*", "haiku*", "fable*")
130
+
131
+
132
+ def is_anthropic_model(model_id: str) -> bool:
133
+ return any(fnmatch.fnmatch(model_id, p) for p in ANTHROPIC_PATTERNS)
134
+
135
+
136
+ def classify_model_family(model_id: str) -> str:
137
+ """Classify a model by its provider family based on model ID patterns."""
138
+ if (
139
+ fnmatch.fnmatch(model_id, "gpt*")
140
+ or fnmatch.fnmatch(model_id, GPT_O_PATTERN)
141
+ or fnmatch.fnmatch(model_id, "ada*")
142
+ or fnmatch.fnmatch(model_id, "v3*")
143
+ or fnmatch.fnmatch(model_id, "*embedding*")
144
+ ):
145
+ return "openai"
146
+
147
+ if is_anthropic_model(model_id):
148
+ return "anthropic"
149
+
150
+ if fnmatch.fnmatch(model_id, GEMINI_PATTERN):
151
+ return "google"
152
+
153
+ return "unknown"