1
- import requests
1
+ """Custom service code execution backend."""
2
+
2
3
from typing import Dict , Any , Optional
4
+ import requests
5
+ from ai_eval .utils import SUPPORTED_LANGUAGE_MAP , LanguageLabels , DEFAULT_HTTP_TIMEOUT
3
6
from .base import CodeExecutionBackend
4
7
5
8
6
9
class CustomServiceBackend (CodeExecutionBackend ):
7
10
"""
8
11
Generic custom code execution backend.
9
12
"""
10
-
11
- def __init__ (
13
+ def __init__ ( # pylint: disable=too-many-positional-arguments
12
14
self ,
13
15
submit_endpoint : str ,
14
16
results_endpoint : str ,
15
17
languages_endpoint : str ,
16
18
api_key : str = "" ,
17
- timeout : int = 30 ,
19
+ timeout : int = DEFAULT_HTTP_TIMEOUT ,
18
20
auth_header_name : str = "Authorization" ,
19
21
auth_scheme : Optional [str ] = "Bearer" ,
20
22
):
@@ -25,8 +27,8 @@ def __init__(
25
27
self .timeout = timeout
26
28
self .auth_header_name = auth_header_name
27
29
self .auth_scheme = auth_scheme
28
- self ._validate_languages ()
29
-
30
+ self ._languages_validated = False
31
+
30
32
def _get_headers (self ) -> Dict [str , str ]:
31
33
"""
32
34
Get headers for API requests.
@@ -38,7 +40,7 @@ def _get_headers(self) -> Dict[str, str]:
38
40
else :
39
41
headers [self .auth_header_name ] = self .api_key
40
42
return headers
41
-
43
+
42
44
def _validate_languages (self ):
43
45
"""
44
46
Validate that static languages are supported by the custom service.
@@ -50,43 +52,56 @@ def _validate_languages(self):
50
52
timeout = self .timeout
51
53
)
52
54
response .raise_for_status ()
53
-
55
+
54
56
service_languages = response .json ()
55
57
# Expected format: [{"id": "92", "name": "Python"}, ...] or [{"id": "python", "name": "Python"}, ...]
56
58
service_language_names = {lang ['name' ].lower () for lang in service_languages }
57
-
58
- from ai_eval .utils import SUPPORTED_LANGUAGE_MAP , LanguageLabels
59
- static_language_names = {name .lower () for name in SUPPORTED_LANGUAGE_MAP .keys ()
60
- if name != LanguageLabels .HTML_CSS }
61
-
59
+
60
+ static_language_names = {
61
+ name .lower () for name in SUPPORTED_LANGUAGE_MAP
62
+ if name != LanguageLabels .HTML_CSS
63
+ }
64
+
62
65
unsupported = static_language_names - service_language_names
63
66
if unsupported :
64
67
raise ValueError (
65
68
f"Custom service does not support languages: { ', ' .join (unsupported )} . "
66
69
)
67
-
70
+
68
71
except (requests .RequestException , KeyError , ValueError ) as e :
69
- raise ValueError (f"Failed to validate supported languages: { e } " )
70
-
72
+ raise ValueError (f"Failed to validate supported languages: { e } " ) from e
73
+
74
+ def _ensure_languages_validated (self ):
75
+ """Validate supported languages lazily once, if endpoint is configured."""
76
+ if self ._languages_validated :
77
+ return
78
+ if not self .languages_endpoint :
79
+ # No endpoint provided; skip validation.
80
+ self ._languages_validated = True
81
+ return
82
+ self ._validate_languages ()
83
+ self ._languages_validated = True
84
+
71
85
def submit_code (self , code : str , language_label : str ) -> str :
72
86
"""
73
87
Submit code to custom service for execution.
74
88
"""
89
+ self ._ensure_languages_validated ()
75
90
# By default, send the language label; services will need to map as needed
76
91
payload = {
77
92
'code' : code ,
78
93
'language' : language_label
79
94
}
80
-
95
+
81
96
try :
82
97
response = requests .post (
83
- self .submit_endpoint ,
84
- json = payload ,
98
+ self .submit_endpoint ,
99
+ json = payload ,
85
100
headers = self ._get_headers (),
86
101
timeout = self .timeout
87
102
)
88
103
response .raise_for_status ()
89
-
104
+
90
105
# Handle different response formats
91
106
result = response .json ()
92
107
if 'submission_id' in result :
@@ -95,28 +110,29 @@ def submit_code(self, code: str, language_label: str) -> str:
95
110
return str (result ['id' ])
96
111
else :
97
112
raise ValueError ("Custom service response missing submission ID" )
98
-
113
+
99
114
except requests .RequestException as e :
100
- raise ValueError (f"Failed to submit code for execution: { e } " )
115
+ raise ValueError (f"Failed to submit code for execution: { e } " ) from e
101
116
except (KeyError , ValueError ) as e :
102
- raise ValueError (f"Invalid response from custom service: { e } " )
103
-
117
+ raise ValueError (f"Invalid response from custom service: { e } " ) from e
118
+
104
119
def get_result (self , submission_id : str ) -> Dict [str , Any ]:
105
120
"""
106
121
Get execution result from custom service.
107
122
"""
123
+ self ._ensure_languages_validated ()
108
124
url = self .results_endpoint .format (submission_id = submission_id )
109
-
125
+
110
126
try :
111
127
response = requests .get (
112
128
url ,
113
129
headers = self ._get_headers (),
114
130
timeout = self .timeout
115
131
)
116
132
response .raise_for_status ()
117
-
133
+
118
134
result = response .json ()
119
-
135
+
120
136
# Map custom service response to standard format
121
137
return {
122
138
'status' : {
@@ -127,9 +143,8 @@ def get_result(self, submission_id: str) -> Dict[str, Any]:
127
143
'stderr' : result .get ('stderr' ),
128
144
'compile_output' : result .get ('compile_error' )
129
145
}
130
-
146
+
131
147
except requests .RequestException as e :
132
- raise ValueError (f"Failed to get submission result: { e } " )
148
+ raise ValueError (f"Failed to get submission result: { e } " ) from e
133
149
except (KeyError , ValueError ) as e :
134
- raise ValueError (f"Invalid response from custom service: { e } " )
135
-
150
+ raise ValueError (f"Invalid response from custom service: { e } " ) from e
0 commit comments