Skip to content

Commit 52de8f4

Browse files
authored
Merge pull request #46 from zhujian0805/main
The change adds the CLAUDE_CODE_ATTRIBUTION_HEADER=0 environment variable to disable attribution headers in the Claude tool configuration. All project requirements have been followed.
2 parents 92f37c6 + 4878c6c commit 52de8f4

4 files changed

Lines changed: 262 additions & 1 deletion

File tree

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
"""Azure OpenAI deployed models fetcher using Azure SDK."""
2+
3+
import logging
4+
import os
5+
from typing import Dict, List, Any
6+
7+
from .env_loader import load_env
8+
9+
# Optional Azure SDK imports - only available if installed
10+
try:
11+
from azure.identity import DefaultAzureCredential
12+
from azure.mgmt.cognitiveservices import CognitiveServicesManagementClient
13+
from azure.mgmt.resource import SubscriptionClient
14+
AZURE_SDK_AVAILABLE = True
15+
except ImportError:
16+
DefaultAzureCredential = None
17+
CognitiveServicesManagementClient = None
18+
SubscriptionClient = None
19+
AZURE_SDK_AVAILABLE = False
20+
21+
logger = logging.getLogger(__name__)
22+
23+
24+
def _fetch_deployed_models_for_subscription(subscription_id: str, credential) -> List[str]:
25+
"""Fetch deployed Azure AI models in a subscription using Azure SDK.
26+
27+
Returns a list of deployed model names (strings).
28+
"""
29+
if not AZURE_SDK_AVAILABLE:
30+
logger.warning("Azure SDK not available. Install azure-identity, azure-mgmt-cognitiveservices, azure-mgmt-resource to enable deployed model discovery.")
31+
return []
32+
33+
results: List[str] = []
34+
try:
35+
client = CognitiveServicesManagementClient(credential, subscription_id)
36+
accounts = list(client.accounts.list())
37+
38+
for acct in accounts:
39+
try:
40+
rg = acct.id.split("/")[4]
41+
acct_name = getattr(acct, 'name', '') or ''
42+
43+
if not acct_name:
44+
continue
45+
46+
# Determine service type to filter for OpenAI accounts
47+
service_type = None
48+
try:
49+
if hasattr(acct, 'kind') and getattr(acct, 'kind'):
50+
service_type = acct.kind
51+
elif hasattr(acct, 'properties') and hasattr(acct.properties, 'kind') and acct.properties.kind:
52+
service_type = acct.properties.kind
53+
elif hasattr(acct, 'sku') and getattr(acct, 'sku') and hasattr(acct.sku, 'name'):
54+
service_type = acct.sku.name
55+
except Exception:
56+
service_type = None
57+
58+
# Only process OpenAI accounts
59+
if not service_type or 'openai' not in service_type.lower():
60+
continue
61+
62+
# Get deployments for this account
63+
for dep in client.deployments.list(resource_group_name=rg, account_name=acct.name):
64+
model = None
65+
try:
66+
model = dep.properties.model
67+
except Exception:
68+
model = getattr(getattr(dep, 'properties', None), 'model', None)
69+
70+
model_name = None
71+
if model and getattr(model, 'name', None):
72+
model_name = model.name
73+
74+
if model_name and model_name not in results:
75+
results.append(model_name)
76+
77+
except Exception as e:
78+
logger.debug(f"Failed to process account {acct_name}: {e}")
79+
continue
80+
81+
# Remove duplicates while preserving order
82+
seen = set()
83+
unique_results = []
84+
for item in results:
85+
if item not in seen:
86+
seen.add(item)
87+
unique_results.append(item)
88+
89+
return unique_results
90+
91+
except Exception as e:
92+
logger.error(f"Failed to fetch deployed models for subscription {subscription_id}: {e}")
93+
return []
94+
95+
96+
def _fetch_all_deployed_models_via_sdk(credential, subscription_ids: List[str] | None = None) -> List[str]:
97+
"""Fetch deployed Azure AI models across subscriptions using Azure SDK.
98+
99+
Returns unique deployed model names.
100+
"""
101+
if not AZURE_SDK_AVAILABLE:
102+
return []
103+
104+
subs = subscription_ids or []
105+
if not subs:
106+
try:
107+
sub_client = SubscriptionClient(credential)
108+
subs = [s.subscription_id for s in sub_client.subscriptions.list()]
109+
except Exception as e:
110+
logger.error(f"Failed to discover subscriptions: {e}")
111+
return []
112+
113+
all_models: List[str] = []
114+
for sid in subs:
115+
try:
116+
models = _fetch_deployed_models_for_subscription(sid, credential)
117+
all_models.extend(models)
118+
except Exception as e:
119+
logger.debug(f"Failed to fetch models for subscription {sid}: {e}")
120+
continue
121+
122+
# Remove duplicates while preserving order
123+
seen = set()
124+
unique_results = []
125+
for item in all_models:
126+
if item not in seen:
127+
seen.add(item)
128+
unique_results.append(item)
129+
130+
return unique_results
131+
132+
133+
def fetch_deployed_azure_openai_models() -> List[str]:
134+
"""Fetch deployed Azure OpenAI models using Azure SDK.
135+
136+
Returns a list of deployed model names.
137+
If Azure SDK is not available or authentication fails, returns empty list.
138+
"""
139+
if not AZURE_SDK_AVAILABLE:
140+
logger.warning("Azure SDK not available for deployed model discovery")
141+
return []
142+
143+
try:
144+
credential = DefaultAzureCredential()
145+
models = _fetch_all_deployed_models_via_sdk(credential)
146+
logger.info(f"Found {len(models)} deployed Azure OpenAI models")
147+
return models
148+
except Exception as e:
149+
logger.error(f"Failed to authenticate to Azure or fetch deployed models: {e}")
150+
return []
151+
152+
153+
def list_models():
154+
"""List deployed Azure OpenAI models. Returns model IDs, one per line."""
155+
load_env()
156+
157+
# Check if we should use SDK-based deployed model discovery
158+
use_sdk = os.environ.get("AZURE_USE_SDK", "true").lower() in ("true", "1", "yes")
159+
160+
if use_sdk:
161+
models = fetch_deployed_azure_openai_models()
162+
if models:
163+
for model in models:
164+
print(model)
165+
return
166+
167+
logger.warning("No deployed models found via Azure SDK. Falling back to HTTP API approach.")
168+
169+
# Fallback: Try HTTP API approach (similar to original implementation)
170+
# This will show all available models, not just deployed ones
171+
endpoint = os.environ.get("endpoint")
172+
if not endpoint:
173+
logger.error("endpoint environment variable is required")
174+
raise SystemExit("endpoint environment variable is required")
175+
176+
# Azure OpenAI endpoints typically end with /openai/v1
177+
# We need to append /models to get the models endpoint
178+
if endpoint.endswith("/openai/v1"):
179+
url = f"{endpoint}/models"
180+
elif endpoint.endswith("/v1"):
181+
# If it ends with /v1 but not /openai/v1, append /models
182+
url = f"{endpoint}/models"
183+
else:
184+
# If it doesn't contain /v1 at all, append /v1/models
185+
if endpoint.endswith("/"):
186+
url = f"{endpoint}v1/models"
187+
else:
188+
url = f"{endpoint}/v1/models"
189+
190+
api_key = os.environ.get("api_key")
191+
if not api_key:
192+
logger.error("api_key environment variable is required")
193+
raise SystemExit("api_key environment variable is required")
194+
195+
# Import here to avoid circular imports
196+
import requests
197+
198+
try:
199+
# Try different API versions if the first one fails
200+
api_versions = ["2024-02-01", "2023-12-01-preview", "2023-05-15"]
201+
202+
models_data = None
203+
for api_version in api_versions:
204+
try:
205+
headers = {
206+
"api-key": api_key,
207+
"Content-Type": "application/json",
208+
}
209+
params = {"api-version": api_version}
210+
r = requests.get(url, headers=headers, params=params, timeout=30)
211+
r.raise_for_status()
212+
models_data = r.json()
213+
break
214+
except requests.exceptions.HTTPError as e:
215+
if e.response.status_code == 401:
216+
# Authentication failed, don't try other versions
217+
raise
218+
logger.debug(f"API version {api_version} failed: {e}")
219+
continue
220+
except Exception as e:
221+
logger.debug(f"API version {api_version} failed: {e}")
222+
continue
223+
224+
if models_data is None:
225+
# Try without api-version parameter
226+
headers = {
227+
"api-key": api_key,
228+
"Content-Type": "application/json",
229+
}
230+
r = requests.get(url, headers=headers, timeout=30)
231+
r.raise_for_status()
232+
models_data = r.json()
233+
234+
for m in models_data.get("data", []):
235+
print(m.get("id"))
236+
237+
except Exception as e:
238+
logger.error(f"Failed to fetch Azure OpenAI models: {e}")
239+
raise SystemExit(f"Failed to fetch Azure OpenAI models: {e}")
240+
241+
242+
if __name__ == "__main__":
243+
list_models()

code_assistant_manager/providers.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@
2929
"keep_proxy_config": false,
3030
"use_proxy": false,
3131
"enabled": false
32+
},
33+
"example-azure-openai": {
34+
"endpoint": "https://example.openai.azure.com/openai/v1",
35+
"api_key_env": "AZURE_OPENAI_API_KEY",
36+
"supported_client": "openai",
37+
"list_models_cmd": "python -m code_assistant_manager.azure_openai_models",
38+
"list_of_models": [],
39+
"keep_proxy_config": false,
40+
"use_proxy": false,
41+
"enabled": true,
42+
"description": "Example Azure OpenAI endpoint"
3243
}
3344
},
3445
"common": {

code_assistant_manager/tools/claude.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def run(self, args: List[str] = None) -> int:
5555
.set_multiple_models({"CLAUDE_MODELS": "primary_model,secondary_model"})
5656
.set_custom_var("DISABLE_NON_ESSENTIAL_MODEL_CALLS", "1")
5757
.set_custom_var("CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC", "1")
58+
.set_custom_var("CLAUDE_CODE_ATTRIBUTION_HEADER", "0")
5859
.set_node_tls_reject_unauthorized()
5960
)
6061
env = env_builder.build()
@@ -75,7 +76,8 @@ def run(self, args: List[str] = None) -> int:
7576
f"ANTHROPIC_SMALL_FAST_MODEL={secondary_model} "
7677
f"ANTHROPIC_DEFAULT_HAIKU_MODEL={primary_model} "
7778
f"DISABLE_NON_ESSENTIAL_MODEL_CALLS=1 "
78-
f"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 {command_str}"
79+
f"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 "
80+
f"CLAUDE_CODE_ATTRIBUTION_HEADER=0 {command_str}"
7981
)
8082
print("")
8183
return self._run_tool_with_env(command, env, "claude", interactive=True)

requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ tomli-w>=1.0.0
1111
typing-extensions>=4.10.0
1212
httpx>=0.27.0
1313

14+
# Azure SDK for deployed model discovery
15+
azure-identity>=1.15.0
16+
azure-mgmt-cognitiveservices>=13.5.0
17+
azure-mgmt-resource>=23.1.0
18+
1419
# Testing dependencies
1520
pytest>=8.0.0
1621
pytest-cov>=4.1.0

0 commit comments

Comments
 (0)