Skip to content

Commit 6d3134c

Browse files
committed
proxy for api-tool
-Only for api_tool for now, if this solution works well then implementation for other tools is required - Need to check api keys creation with the current proxies - Show connection string example at creation - locale needs updates for other languages
1 parent 0e31329 commit 6d3134c

File tree

21 files changed

+1641
-8
lines changed

21 files changed

+1641
-8
lines changed

application/agents/agent_creator.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ def create_agent(cls, type, *args, **kwargs):
1111
agent_class = cls.agents.get(type.lower())
1212
if not agent_class:
1313
raise ValueError(f"No agent class found for type {type}")
14+
config = kwargs.pop('config', None)
15+
if isinstance(config, dict) and 'proxy_id' in config and 'proxy_id' not in kwargs:
16+
kwargs['proxy_id'] = config['proxy_id']
1417
return agent_class(*args, **kwargs)

application/agents/base.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def __init__(
1717
api_key,
1818
user_api_key=None,
1919
decoded_token=None,
20+
proxy_id=None,
2021
):
2122
self.endpoint = endpoint
2223
self.llm = LLMCreator.create_llm(
@@ -30,6 +31,7 @@ def __init__(
3031
self.tools = []
3132
self.tool_config = {}
3233
self.tool_calls = []
34+
self.proxy_id = proxy_id
3335

3436
def gen(self, *args, **kwargs) -> Generator[Dict, None, None]:
3537
raise NotImplementedError('Method "gen" must be implemented in the child class')
@@ -41,6 +43,11 @@ def _get_user_tools(self, user="local"):
4143
user_tools = user_tools_collection.find({"user": user, "status": True})
4244
user_tools = list(user_tools)
4345
tools_by_id = {str(tool["_id"]): tool for tool in user_tools}
46+
if hasattr(self, 'proxy_id') and self.proxy_id:
47+
for tool_id, tool in tools_by_id.items():
48+
if 'config' not in tool:
49+
tool['config'] = {}
50+
tool['config']['proxy_id'] = self.proxy_id
4451
return tools_by_id
4552

4653
def _build_tool_parameters(self, action):
@@ -126,6 +133,7 @@ def _execute_tool_action(self, tools_dict, call):
126133
"method": tool_data["config"]["actions"][action_name]["method"],
127134
"headers": headers,
128135
"query_params": query_params,
136+
"proxy_id": self.proxy_id,
129137
}
130138
if tool_data["name"] == "api_tool"
131139
else tool_data["config"]

application/agents/classic_agent.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ def __init__(
1818
prompt="",
1919
chat_history=None,
2020
decoded_token=None,
21+
proxy_id=None,
2122
):
2223
super().__init__(
23-
endpoint, llm_name, gpt_model, api_key, user_api_key, decoded_token
24+
endpoint, llm_name, gpt_model, api_key, user_api_key, decoded_token, proxy_id
2425
)
2526
self.user = decoded_token.get("sub")
2627
self.prompt = prompt

application/agents/tools/api_tool.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,43 @@ def execute_action(self, action_name, **kwargs):
2323
)
2424

2525
def _make_api_call(self, url, method, headers, query_params, body):
26+
sanitized_headers = {}
27+
for key, value in headers.items():
28+
if isinstance(value, str):
29+
sanitized_value = value.encode('latin-1', errors='ignore').decode('latin-1')
30+
sanitized_headers[key] = sanitized_value
31+
else:
32+
sanitized_headers[key] = value
33+
2634
if query_params:
2735
url = f"{url}?{requests.compat.urlencode(query_params)}"
2836
if isinstance(body, dict):
2937
body = json.dumps(body)
38+
response = None
3039
try:
3140
print(f"Making API call: {method} {url} with body: {body}")
3241
if body == "{}":
3342
body = None
34-
response = requests.request(method, url, headers=headers, data=body)
43+
44+
proxy_id = self.config.get("proxy_id", None)
45+
request_kwargs = {
46+
'method': method,
47+
'url': url,
48+
'headers': sanitized_headers,
49+
'data': body
50+
}
51+
try:
52+
if proxy_id:
53+
from application.agents.tools.proxy_handler import apply_proxy_to_request
54+
response = apply_proxy_to_request(
55+
requests.request,
56+
proxy_id=proxy_id,
57+
**request_kwargs
58+
)
59+
else:
60+
response = requests.request(**request_kwargs)
61+
except ImportError:
62+
response = requests.request(**request_kwargs)
3563
response.raise_for_status()
3664
content_type = response.headers.get(
3765
"Content-Type", "application/json"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import logging
2+
import requests
3+
from typing import Dict, Optional
4+
from bson.objectid import ObjectId
5+
6+
from application.core.mongo_db import MongoDB
7+
8+
logger = logging.getLogger(__name__)
9+
10+
# Get MongoDB connection
11+
mongo = MongoDB.get_client()
12+
db = mongo["docsgpt"]
13+
proxies_collection = db["proxies"]
14+
15+
def get_proxy_config(proxy_id: str) -> Optional[Dict[str, str]]:
16+
"""
17+
Retrieve proxy configuration from the database.
18+
19+
Args:
20+
proxy_id: The ID of the proxy configuration
21+
22+
Returns:
23+
A dictionary with proxy configuration or None if not found
24+
"""
25+
if not proxy_id or proxy_id == "none":
26+
return None
27+
28+
try:
29+
if ObjectId.is_valid(proxy_id):
30+
proxy_config = proxies_collection.find_one({"_id": ObjectId(proxy_id)})
31+
if proxy_config and "connection" in proxy_config:
32+
connection_str = proxy_config["connection"].strip()
33+
if connection_str:
34+
# Format proxy for requests library
35+
return {
36+
"http": connection_str,
37+
"https": connection_str
38+
}
39+
return None
40+
except Exception as e:
41+
logger.error(f"Error retrieving proxy configuration: {e}")
42+
return None
43+
44+
def apply_proxy_to_request(request_func, proxy_id=None, **kwargs):
45+
"""
46+
Apply proxy configuration to a requests function if available.
47+
This is a minimal wrapper that doesn't change the function signature.
48+
49+
Args:
50+
request_func: The requests function to call (e.g., requests.get, requests.post)
51+
proxy_id: Optional proxy ID to use
52+
**kwargs: Arguments to pass to the request function
53+
54+
Returns:
55+
The response from the request
56+
"""
57+
if proxy_id:
58+
proxy_config = get_proxy_config(proxy_id)
59+
if proxy_config:
60+
kwargs['proxies'] = proxy_config
61+
logger.info(f"Using proxy for request")
62+
63+
return request_func(**kwargs)

application/api/answer/routes.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,9 @@ class Stream(Resource):
335335
"prompt_id": fields.String(
336336
required=False, default="default", description="Prompt ID"
337337
),
338+
"proxy_id": fields.String(
339+
required=False, description="Proxy ID to use for API calls"
340+
),
338341
"chunks": fields.Integer(
339342
required=False, default=2, description="Number of chunks"
340343
),
@@ -376,6 +379,7 @@ def post(self):
376379
)
377380
conversation_id = data.get("conversation_id")
378381
prompt_id = data.get("prompt_id", "default")
382+
proxy_id = data.get("proxy_id", None)
379383

380384
index = data.get("index", None)
381385
chunks = int(data.get("chunks", 2))
@@ -386,6 +390,7 @@ def post(self):
386390
data_key = get_data_from_api_key(data["api_key"])
387391
chunks = int(data_key.get("chunks", 2))
388392
prompt_id = data_key.get("prompt_id", "default")
393+
proxy_id = data_key.get("proxy_id", None)
389394
source = {"active_docs": data_key.get("source")}
390395
retriever_name = data_key.get("retriever", retriever_name)
391396
user_api_key = data["api_key"]
@@ -422,6 +427,7 @@ def post(self):
422427
api_key=settings.API_KEY,
423428
user_api_key=user_api_key,
424429
prompt=prompt,
430+
proxy_id=proxy_id,
425431
chat_history=history,
426432
decoded_token=decoded_token,
427433
)
@@ -496,6 +502,9 @@ class Answer(Resource):
496502
"prompt_id": fields.String(
497503
required=False, default="default", description="Prompt ID"
498504
),
505+
"proxy_id": fields.String(
506+
required=False, description="Proxy ID to use for API calls"
507+
),
499508
"chunks": fields.Integer(
500509
required=False, default=2, description="Number of chunks"
501510
),
@@ -527,6 +536,7 @@ def post(self):
527536
)
528537
conversation_id = data.get("conversation_id")
529538
prompt_id = data.get("prompt_id", "default")
539+
proxy_id = data.get("proxy_id", None)
530540
chunks = int(data.get("chunks", 2))
531541
token_limit = data.get("token_limit", settings.DEFAULT_MAX_HISTORY)
532542
retriever_name = data.get("retriever", "classic")
@@ -535,6 +545,7 @@ def post(self):
535545
data_key = get_data_from_api_key(data["api_key"])
536546
chunks = int(data_key.get("chunks", 2))
537547
prompt_id = data_key.get("prompt_id", "default")
548+
proxy_id = data_key.get("proxy_id", None)
538549
source = {"active_docs": data_key.get("source")}
539550
retriever_name = data_key.get("retriever", retriever_name)
540551
user_api_key = data["api_key"]
@@ -569,6 +580,7 @@ def post(self):
569580
api_key=settings.API_KEY,
570581
user_api_key=user_api_key,
571582
prompt=prompt,
583+
proxy_id=proxy_id,
572584
chat_history=history,
573585
decoded_token=decoded_token,
574586
)

0 commit comments

Comments
 (0)