2828DEFAULT_TEST_INPUT = "Hello, please introduce yourself briefly."
2929SERVER_PORT = 8088
3030SERVER_URL = f"http://localhost:{ SERVER_PORT } "
31- STARTUP_TIMEOUT = 30 # seconds
31+ STARTUP_TIMEOUT = 30 # seconds for Python
32+ DOTNET_STARTUP_TIMEOUT = 60 # seconds for C# (longer due to JIT)
33+ DOTNET_BUILD_TIMEOUT = 300 # 5 minutes for NuGet restore + compile
3234REQUEST_TIMEOUT = 120 # seconds
3335
3436
@@ -37,18 +39,71 @@ def extract_test_input(agent_yaml_path: Path) -> str:
3739 try :
3840 with open (agent_yaml_path ) as f :
3941 config = yaml .safe_load (f )
40-
42+
4143 examples = config .get ("metadata" , {}).get ("example" , [])
4244 if examples and isinstance (examples , list ):
4345 for example in examples :
4446 if example .get ("role" ) == "user" and example .get ("content" ):
4547 return example ["content" ]
4648 except Exception as e :
4749 print (f"Warning: Could not parse agent.yaml for test input: { e } " )
48-
50+
4951 return DEFAULT_TEST_INPUT
5052
5153
54+ def detect_language (sample_path : Path ) -> str :
55+ """Detect the language/framework of a sample based on marker files."""
56+ if (sample_path / "main.py" ).exists () and (sample_path / "requirements.txt" ).exists ():
57+ return "python"
58+
59+ csproj_files = list (sample_path .glob ("*.csproj" ))
60+ if csproj_files and (sample_path / "Program.cs" ).exists ():
61+ return "csharp"
62+
63+ return "unknown"
64+
65+
66+ def find_csproj (sample_path : Path ) -> Path | None :
67+ """Find the .csproj file in the sample directory."""
68+ csproj_files = list (sample_path .glob ("*.csproj" ))
69+ return csproj_files [0 ] if csproj_files else None
70+
71+
72+ def build_csharp_sample (sample_path : Path , csproj_path : Path ) -> tuple [bool , str ]:
73+ """Build a C# sample using dotnet build.
74+
75+ Returns:
76+ Tuple of (success, error_message)
77+ """
78+ print (f"Building C# project { csproj_path .name } ..." )
79+ try :
80+ result = subprocess .run (
81+ ["dotnet" , "build" , str (csproj_path ), "-c" , "Release" ],
82+ cwd = str (sample_path ),
83+ capture_output = True ,
84+ text = True ,
85+ timeout = DOTNET_BUILD_TIMEOUT
86+ )
87+ if result .returncode != 0 :
88+ return False , f"Build failed: { result .stderr [:1000 ]} "
89+ return True , ""
90+ except subprocess .TimeoutExpired :
91+ return False , f"Build timed out after { DOTNET_BUILD_TIMEOUT } seconds"
92+ except FileNotFoundError :
93+ return False , "dotnet CLI not found. Ensure .NET SDK is installed."
94+
95+
96+ def start_csharp_server (sample_path : Path , csproj_path : Path ) -> subprocess .Popen :
97+ """Start a C# sample server using dotnet run."""
98+ return subprocess .Popen (
99+ ["dotnet" , "run" , "--project" , str (csproj_path ), "-c" , "Release" , "--no-build" ],
100+ cwd = str (sample_path ),
101+ stdout = subprocess .PIPE ,
102+ stderr = subprocess .PIPE ,
103+ text = True
104+ )
105+
106+
52107def wait_for_server (timeout : int = STARTUP_TIMEOUT ) -> bool :
53108 """Wait for the server to be ready to accept connections."""
54109 start_time = time .time ()
@@ -97,57 +152,88 @@ def run_test(sample_path: Path) -> dict:
97152 "error" : None ,
98153 "details" : {}
99154 }
100-
155+
101156 agent_yaml_path = sample_path / "agent.yaml"
102- main_py_path = sample_path / "main.py"
103- requirements_path = sample_path / "requirements.txt"
104-
105- # Validate required files
157+
158+ # Validate agent.yaml exists
106159 if not agent_yaml_path .exists ():
107160 result ["error" ] = "agent.yaml not found"
108161 return result
109-
110- if not main_py_path .exists ():
111- result ["error" ] = "main.py not found"
162+
163+ # Detect language
164+ language = detect_language (sample_path )
165+ result ["details" ]["language" ] = language
166+
167+ if language == "unknown" :
168+ result ["error" ] = "Could not detect sample language (no main.py or *.csproj found)"
112169 return result
113-
170+
114171 # Extract test input
115172 test_input = extract_test_input (agent_yaml_path )
116173 result ["details" ]["test_input" ] = test_input [:100 ] + "..." if len (test_input ) > 100 else test_input
117-
118- # Install dependencies
119- print (f"Installing dependencies from { requirements_path } ..." )
120- try :
121- subprocess .run (
122- [sys .executable , "-m" , "pip" , "install" , "-r" , str (requirements_path ), "-q" ],
123- check = True ,
124- capture_output = True ,
174+
175+ # Language-specific setup and server start
176+ if language == "python" :
177+ main_py_path = sample_path / "main.py"
178+ requirements_path = sample_path / "requirements.txt"
179+
180+ if not main_py_path .exists ():
181+ result ["error" ] = "main.py not found"
182+ return result
183+
184+ # Install dependencies
185+ print (f"Installing dependencies from { requirements_path } ..." )
186+ try :
187+ subprocess .run (
188+ [sys .executable , "-m" , "pip" , "install" , "-r" , str (requirements_path ), "-q" ],
189+ check = True ,
190+ capture_output = True ,
191+ text = True
192+ )
193+ except subprocess .CalledProcessError as e :
194+ result ["error" ] = f"Failed to install dependencies: { e .stderr } "
195+ return result
196+
197+ # Start server
198+ print (f"Starting Python server for { sample_path .name } ..." )
199+ server_process = subprocess .Popen (
200+ [sys .executable , str (main_py_path )],
201+ cwd = str (sample_path ),
202+ stdout = subprocess .PIPE ,
203+ stderr = subprocess .PIPE ,
125204 text = True
126205 )
127- except subprocess .CalledProcessError as e :
128- result ["error" ] = f"Failed to install dependencies: { e .stderr } "
129- return result
130-
131- # Start the server
132- print (f"Starting server for { sample_path .name } ..." )
133- server_process = subprocess .Popen (
134- [sys .executable , str (main_py_path )],
135- cwd = str (sample_path ),
136- stdout = subprocess .PIPE ,
137- stderr = subprocess .PIPE ,
138- text = True
139- )
140-
206+ startup_timeout = STARTUP_TIMEOUT
207+
208+ elif language == "csharp" :
209+ csproj_path = find_csproj (sample_path )
210+ if not csproj_path :
211+ result ["error" ] = "No .csproj file found"
212+ return result
213+
214+ # Build the project
215+ build_success , build_error = build_csharp_sample (sample_path , csproj_path )
216+ if not build_success :
217+ result ["error" ] = build_error
218+ return result
219+
220+ result ["details" ]["build" ] = "success"
221+
222+ # Start server
223+ print (f"Starting C# server for { sample_path .name } ..." )
224+ server_process = start_csharp_server (sample_path , csproj_path )
225+ startup_timeout = DOTNET_STARTUP_TIMEOUT
226+
141227 try :
142228 # Wait for server to be ready
143- print (f"Waiting for server to start (timeout: { STARTUP_TIMEOUT } s)..." )
144- if not wait_for_server (STARTUP_TIMEOUT ):
229+ print (f"Waiting for server to start (timeout: { startup_timeout } s)..." )
230+ if not wait_for_server (startup_timeout ):
145231 # Check if process died
146232 if server_process .poll () is not None :
147233 stdout , stderr = server_process .communicate ()
148234 result ["error" ] = f"Server process exited unexpectedly. stderr: { stderr [:500 ]} "
149235 else :
150- result ["error" ] = f"Server did not start within { STARTUP_TIMEOUT } seconds"
236+ result ["error" ] = f"Server did not start within { startup_timeout } seconds"
151237 return result
152238
153239 result ["details" ]["server_started" ] = True
0 commit comments