hiagent-eva 0.1.0__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.
@@ -0,0 +1,169 @@
1
+ ### Python template
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+
7
+ # C extensions
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ #Pipfile.lock
97
+
98
+ # poetry
99
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
103
+ #poetry.lock
104
+
105
+ # pdm
106
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
107
+ #pdm.lock
108
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
109
+ # in version control.
110
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
111
+ .pdm.toml
112
+ .pdm-python
113
+ .pdm-build/
114
+
115
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
116
+ __pypackages__/
117
+
118
+ # Celery stuff
119
+ celerybeat-schedule
120
+ celerybeat.pid
121
+
122
+ # SageMath parsed files
123
+ *.sage.py
124
+
125
+ # Environments
126
+ .env
127
+ .venv
128
+ env/
129
+ venv/
130
+ ENV/
131
+ env.bak/
132
+ venv.bak/
133
+
134
+ # Spyder project settings
135
+ .spyderproject
136
+ .spyproject
137
+
138
+ # Rope project settings
139
+ .ropeproject
140
+
141
+ # mkdocs documentation
142
+ /site
143
+
144
+ # mypy
145
+ .mypy_cache/
146
+ .dmypy.json
147
+ dmypy.json
148
+
149
+ # Pyre type checker
150
+ .pyre/
151
+
152
+ # pytype static type analyzer
153
+ .pytype/
154
+
155
+ # Cython debug symbols
156
+ cython_debug/
157
+
158
+ # PyCharm
159
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
160
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
161
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
162
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
163
+ #.idea/
164
+
165
+ chainlit.md
166
+ .chainlit/
167
+ .vscode
168
+ .idea
169
+ license.py
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: hiagent-eva
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.10
File without changes
@@ -0,0 +1,86 @@
1
+ # Copyright (c) 2024 Bytedance Ltd. and/or its affiliates
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """
15
+ Eva SDK - Evaluation Service SDK
16
+ """
17
+
18
+ from ..api.hiagent_api.eva import EvaService
19
+ from ..api.hiagent_api.eva_types import (
20
+ AgentModeStrategy,
21
+ CaseData,
22
+ CreateEvaTaskRequest,
23
+ CreateEvaTaskResponse,
24
+ DatasetTaskConfig,
25
+ EvaDatasetColumn,
26
+ EvaDatasetConversationItem,
27
+ EvaTargetCustomAPPConfig,
28
+ EvaTargetType,
29
+ EvaTaskItem,
30
+ EvaTaskItemDataset,
31
+ EvaTaskItemRuleset,
32
+ EvaTaskItemRulesetRule,
33
+ EvaTaskItemTarget,
34
+ EvaTaskResultTargetContentPair,
35
+ EvaTaskResultUpdateTargetContent,
36
+ EvaTaskStatus,
37
+ EvaTaskTarget,
38
+ ExecEvaTaskRowGroupRequest,
39
+ GetEvaTaskReportRequest,
40
+ GetEvaTaskReportResponse,
41
+ GetEvaTaskRequest,
42
+ GetEvaTaskResponse,
43
+ InferenceResult,
44
+ ListEvaDatasetColumnsRequest,
45
+ ListEvaDatasetColumnsResponse,
46
+ ListEvaDatasetConversationsRequest,
47
+ ListEvaDatasetConversationsResponse,
48
+ ModelAgentConfig,
49
+ )
50
+ from .client import Client
51
+
52
+ __version__ = "1.0.0"
53
+
54
+ __all__ = [
55
+ "Client",
56
+ "EvaService",
57
+ "CreateEvaTaskRequest",
58
+ "ListEvaDatasetConversationsRequest",
59
+ "ListEvaDatasetColumnsRequest",
60
+ "ExecEvaTaskRowGroupRequest",
61
+ "GetEvaTaskReportRequest",
62
+ "GetEvaTaskRequest",
63
+ "CreateEvaTaskResponse",
64
+ "ListEvaDatasetConversationsResponse",
65
+ "ListEvaDatasetColumnsResponse",
66
+ "GetEvaTaskReportResponse",
67
+ "GetEvaTaskResponse",
68
+ "EvaTaskTarget",
69
+ "DatasetTaskConfig",
70
+ "EvaDatasetColumn",
71
+ "EvaDatasetConversationItem",
72
+ "EvaTaskResultTargetContentPair",
73
+ "EvaTaskResultUpdateTargetContent",
74
+ "InferenceResult",
75
+ "EvaTaskItem",
76
+ "EvaTaskItemRuleset",
77
+ "EvaTaskItemRulesetRule",
78
+ "EvaTaskItemDataset",
79
+ "EvaTaskItemTarget",
80
+ "ModelAgentConfig",
81
+ "EvaTargetCustomAPPConfig",
82
+ "EvaTargetType",
83
+ "EvaTaskStatus",
84
+ "CaseData",
85
+ "AgentModeStrategy",
86
+ ]
@@ -0,0 +1,487 @@
1
+ # coding:utf-8
2
+ # Copyright (c) 2024 Bytedance Ltd. and/or its affiliates
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import logging
16
+ import time
17
+ from typing import Callable, Dict, List, Optional
18
+
19
+ from tenacity import before_sleep_log, retry, stop_after_attempt, wait_fixed
20
+
21
+ from libs.observe.hiagent_observe.client import AuthSession
22
+
23
+ from ..api.hiagent_api import eva_types
24
+ from ..api.hiagent_api.eva import EvaService
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ def init(endpoint: str, ak: str, sk: str, workspace_id: str, app_id: str):
30
+ """
31
+ Initialize client configuration
32
+ """
33
+ return Client(endpoint, ak, sk, workspace_id, app_id)
34
+
35
+
36
+ class Client:
37
+ """Eva SDK Client Class"""
38
+
39
+ def __init__(self, endpoint: str, ak: str, sk: str, workspace_id: str, app_id: str):
40
+ """
41
+ Initialize client configuration
42
+
43
+ Args:
44
+ endpoint: API endpoint
45
+ ak: Access Key
46
+ sk: Secret Key
47
+ workspace_id: Workspace ID
48
+ app_id: Application ID
49
+ """
50
+ self.endpoint = endpoint
51
+ self.workspace_id = workspace_id
52
+ self.app_id = app_id
53
+ self.logger = logging.getLogger(__name__)
54
+
55
+ # Initialize Eva service
56
+ self.eva_service = EvaService(endpoint=endpoint)
57
+
58
+ self.eva_service.set_ak(ak)
59
+ self.eva_service.set_sk(sk)
60
+
61
+ self.logger.info(f"Eva client initialized with endpoint: {endpoint}")
62
+
63
+ def create_task(
64
+ self,
65
+ dataset_id: str,
66
+ task_name: str,
67
+ ruleset_id: str,
68
+ description: str = "",
69
+ model_agent_config: Optional[eva_types.ModelAgentConfig] = None,
70
+ run_immediately: bool = True,
71
+ ) -> eva_types.CreateEvaTaskResponse:
72
+ """
73
+ Create evaluation task
74
+
75
+ Args:
76
+ dataset_id: Dataset ID
77
+ task_name: Task name
78
+ ruleset_id: Ruleset ID
79
+ description: Task description
80
+ model_agent_config: Model agent configuration (optional, will be automatically assembled into complete configuration using app_id)
81
+ run_immediately: Whether to run immediately
82
+
83
+ Returns:
84
+ CreateEvaTaskResponse: Create task response
85
+ """
86
+ if not self.eva_service:
87
+ raise ValueError("Client not initialized. Call init() first.")
88
+
89
+ # Automatically create evaluation target, using app_id as target_id
90
+ # If ModelAgentConfig is provided, assemble it into complete EvaTargetCustomAPPConfig
91
+ if model_agent_config:
92
+ target_config = eva_types.EvaTargetCustomAPPConfig(
93
+ AppID=self.app_id, ModelAgentConfig=model_agent_config.model_dump()
94
+ )
95
+ target_config_dict = target_config.model_dump()
96
+ else:
97
+ target_config_dict = {}
98
+
99
+ targets = [
100
+ eva_types.EvaTaskTarget(
101
+ Type="CustomAPP",
102
+ TargetID=self.app_id,
103
+ TargetName=f"App-{self.app_id}",
104
+ TargetConfig=target_config_dict,
105
+ QPS=1,
106
+ )
107
+ ]
108
+
109
+ request = eva_types.CreateEvaTaskRequest(
110
+ WorkspaceID=self.workspace_id,
111
+ Name=task_name,
112
+ Description=description,
113
+ Targets=targets,
114
+ DatasetID=dataset_id,
115
+ RulesetID=ruleset_id,
116
+ RunImmediately=run_immediately,
117
+ )
118
+
119
+ return self.eva_service.CreateEvaTask(request)
120
+
121
+ def list_dataset_conversations(
122
+ self, dataset_id: str, page_number: int = 1, page_size: int = 20
123
+ ) -> eva_types.ListEvaDatasetConversationsResponse:
124
+ """
125
+ List dataset conversations
126
+
127
+ Args:
128
+ dataset_id: Dataset ID
129
+ page_number: Page number
130
+ page_size: Page size
131
+
132
+ Returns:
133
+ ListEvaDatasetConversationsResponse: Dataset conversation response
134
+ """
135
+ if not self.eva_service:
136
+ raise ValueError("Client not initialized. Call init() first.")
137
+
138
+ request = eva_types.ListEvaDatasetConversationsRequest(
139
+ WorkspaceID=self.workspace_id,
140
+ DatasetID=dataset_id,
141
+ PageNumber=page_number,
142
+ PageSize=page_size,
143
+ )
144
+
145
+ return self.eva_service.ListEvaDatasetConversations(request)
146
+
147
+ def list_dataset_columns(
148
+ self, dataset_id: str
149
+ ) -> eva_types.ListEvaDatasetColumnsResponse:
150
+ """
151
+ Get dataset column information
152
+
153
+ Args:
154
+ dataset_id: Dataset ID
155
+
156
+ Returns:
157
+ ListEvaDatasetColumnsResponse: Dataset column information response
158
+ """
159
+ if not self.eva_service:
160
+ raise ValueError("Client not initialized. Call init() first.")
161
+
162
+ request = eva_types.ListEvaDatasetColumnsRequest(
163
+ WorkspaceID=self.workspace_id,
164
+ DatasetID=dataset_id,
165
+ )
166
+
167
+ return self.eva_service.ListEvaDatasetColumns(request)
168
+
169
+ def submit_task_row_group_results(
170
+ self,
171
+ task_id: str,
172
+ row_id: str,
173
+ target_results: Optional[
174
+ List[eva_types.EvaTaskResultUpdateTargetContent]
175
+ ] = None,
176
+ ) -> eva_types.ExecEvaTaskRowGroupResponse:
177
+ """
178
+ Submit evaluation task row group results
179
+
180
+ Args:
181
+ task_id: Task ID
182
+ row_id: Row ID
183
+ target_results: Target results list
184
+
185
+ Returns:
186
+ ExecEvaTaskRowGroupResponse: Execution response
187
+ """
188
+ if not self.eva_service:
189
+ raise ValueError("Client not initialized. Call init() first.")
190
+
191
+ request = eva_types.ExecEvaTaskRowGroupRequest(
192
+ WorkspaceID=self.workspace_id,
193
+ TaskID=task_id,
194
+ RowID=row_id,
195
+ TargetResults=target_results,
196
+ )
197
+
198
+ return self.eva_service.ExecEvaTaskRowGroup(request)
199
+
200
+ @retry(
201
+ stop=stop_after_attempt(3),
202
+ wait=wait_fixed(1),
203
+ before_sleep=before_sleep_log(logger, logging.INFO),
204
+ )
205
+ def get_task_report(self, task_id: str) -> eva_types.GetEvaTaskReportResponse:
206
+ """
207
+ Get task report (with retry mechanism: 1 second interval, 3 attempts)
208
+
209
+ Args:
210
+ task_id: Task ID
211
+
212
+ Returns:
213
+ GetEvaTaskReportResponse: Task report response
214
+ """
215
+ if not self.eva_service:
216
+ raise ValueError("Client not initialized. Call init() first.")
217
+
218
+ request = eva_types.GetEvaTaskReportRequest(
219
+ WorkspaceID=self.workspace_id, TaskID=task_id
220
+ )
221
+
222
+ return self.eva_service.GetEvaTaskReport(request)
223
+
224
+ def _convert_to_case_data_list(
225
+ self,
226
+ conversation_item: eva_types.EvaDatasetConversationItem,
227
+ columns: List[eva_types.EvaDatasetColumn],
228
+ ) -> List[eva_types.CaseData]:
229
+ """
230
+ Convert raw conversation item to case data list
231
+
232
+ Args:
233
+ conversation_item: Raw conversation item
234
+ columns: Column information
235
+
236
+ Returns:
237
+ Converted case data list
238
+ """
239
+ # Create column ID to column name mapping
240
+ column_map = {col.ID: col.Name for col in columns}
241
+
242
+ # Organize data by rounds
243
+ rounds_data: Dict[int, Dict[str, eva_types.CellContent]] = {}
244
+
245
+ for data_cell in conversation_item.DataRow:
246
+ column_name = column_map.get(
247
+ data_cell.ColumnID, f"Column_{data_cell.ColumnID}"
248
+ )
249
+
250
+ for conv_data in data_cell.ConversationGroup:
251
+ round_num = conv_data.Round
252
+ if round_num not in rounds_data:
253
+ rounds_data[round_num] = {}
254
+ rounds_data[round_num][column_name] = self._convert_to_cell_content(
255
+ conv_data.AtomicData
256
+ )
257
+
258
+ # Create CellContent list in round order
259
+ sorted_rounds = sorted(rounds_data.keys())
260
+ case_data_list = []
261
+
262
+ for round_num in sorted_rounds:
263
+ case_data = eva_types.CaseData(**rounds_data[round_num])
264
+ case_data_list.append(case_data)
265
+
266
+ return case_data_list
267
+
268
+ def _convert_to_cell_content(
269
+ self, atomic_data: eva_types.EvaDatasetAtomicData
270
+ ) -> eva_types.CellContent:
271
+ """
272
+ Convert atomic data to cell content
273
+ """
274
+ content = []
275
+ if atomic_data.Type == eva_types.DatasetAtomicDataType.TEXT:
276
+ content.append(
277
+ eva_types.CellContentPart(
278
+ Type=eva_types.CellContentPartType.TEXT,
279
+ Text=atomic_data.TextData,
280
+ )
281
+ )
282
+ elif atomic_data.Type == eva_types.DatasetAtomicDataType.IMAGE:
283
+ content.extend(
284
+ [
285
+ eva_types.CellContentPart(
286
+ Type=eva_types.CellContentPartType.IMAGE_URL,
287
+ ImageURL=image.URL,
288
+ )
289
+ for image in atomic_data.ImageData
290
+ ]
291
+ )
292
+ elif atomic_data.Type == eva_types.DatasetAtomicDataType.FILE:
293
+ content.extend(
294
+ [
295
+ eva_types.CellContentPart(
296
+ Type=eva_types.CellContentPartType.VIDEO_URL,
297
+ VideoURL=file.URL,
298
+ )
299
+ for file in atomic_data.Files
300
+ ]
301
+ )
302
+ return content
303
+
304
+ def run_evaluation(
305
+ self,
306
+ dataset_id: str,
307
+ task_name: str,
308
+ inference_function: Callable[
309
+ [List[eva_types.CaseData]], List[eva_types.InferenceResult]
310
+ ],
311
+ ruleset_id: str,
312
+ target_config: Optional[eva_types.ModelAgentConfig] = None,
313
+ max_conversations: int = 10,
314
+ ) -> eva_types.GetEvaTaskReportResponse:
315
+ """
316
+ Run complete evaluation process
317
+
318
+ Args:
319
+ dataset_id: Dataset ID
320
+ task_name: Task name
321
+ inference_function: Inference function that receives case data list and returns user inference results list
322
+ ruleset_id: Ruleset ID
323
+ target_config: Model agent configuration (optional)
324
+ max_conversations: Maximum number of conversations
325
+
326
+ Returns:
327
+ GetEvaTaskReportResponse: Evaluation report
328
+ """
329
+ try:
330
+ # 1. Create evaluation task
331
+ self.logger.info(f"Creating evaluation task: {task_name}")
332
+ task_response = self.create_task(
333
+ dataset_id=dataset_id,
334
+ task_name=task_name,
335
+ ruleset_id=ruleset_id,
336
+ model_agent_config=target_config,
337
+ )
338
+ task_id = task_response.TaskID
339
+ self.logger.info(f"Task created successfully: {task_id}")
340
+
341
+ # 2. Get dataset column information
342
+ self.logger.info("Fetching dataset columns...")
343
+ columns_response = self.list_dataset_columns(dataset_id)
344
+ columns = columns_response.Columns
345
+ self.logger.info(
346
+ f"Fetched {len(columns)} columns: {[col.Name for col in columns]}"
347
+ )
348
+
349
+ # 3. Get dataset conversations
350
+ self.logger.info("Fetching dataset conversations...")
351
+ conversations_response = self.list_dataset_conversations(
352
+ dataset_id=dataset_id, page_size=max_conversations
353
+ )
354
+ conversation_items = conversations_response.Items
355
+ self.logger.info(f"Fetched {len(conversation_items)} conversation items")
356
+
357
+ # 4. Execute inference and submit results
358
+ self.logger.info("Running inference and submitting results...")
359
+ time.sleep(2)
360
+ for conversation_item in conversation_items:
361
+ # Convert to case data list
362
+ case_data_list = self._convert_to_case_data_list(
363
+ conversation_item, columns
364
+ )
365
+
366
+ # Call inference function, passing case data list
367
+ target_content_pairs = self._execute_inference_with_wrapper(
368
+ inference_function, case_data_list, conversation_item.RowID
369
+ )
370
+
371
+ # Create target results
372
+ target_results = [
373
+ eva_types.EvaTaskResultUpdateTargetContent(
374
+ TargetType=eva_types.EvaTargetType.CUSTOM_APP,
375
+ TargetID=self.app_id,
376
+ Results=target_content_pairs,
377
+ )
378
+ ]
379
+
380
+ # Submit results
381
+ self.submit_task_row_group_results(
382
+ task_id, conversation_item.RowID, target_results
383
+ )
384
+ self.logger.debug(
385
+ f"Results submitted for row {conversation_item.RowID}"
386
+ )
387
+
388
+ # 5. Wait for processing to complete
389
+ self.logger.info("Waiting for evaluation to complete...")
390
+ self._wait_task_finished(task_id=task_id)
391
+
392
+ # 6. Get evaluation report
393
+ report = self.get_task_report(task_id)
394
+ self.logger.info(f"Evaluation completed with status: {report.Status}")
395
+
396
+ return report
397
+
398
+ except Exception as e:
399
+ self.logger.error(f"Evaluation failed: {e}", exc_info=True)
400
+ raise
401
+
402
+ def _execute_inference_with_wrapper(
403
+ self,
404
+ inference_function: Callable[
405
+ [List[eva_types.CaseData]], List[eva_types.InferenceResult]
406
+ ],
407
+ case_data_list: List[eva_types.CaseData],
408
+ row_id: str,
409
+ ) -> List[eva_types.EvaTaskResultTargetContentPair]:
410
+ """
411
+ Execute inference function and wrap results
412
+
413
+ Args:
414
+ inference_function: User-provided inference function
415
+ case_data_list: Case data list
416
+ row_id: Row ID
417
+
418
+ Returns:
419
+ Wrapped target content pair list
420
+ """
421
+ import time
422
+
423
+ start_time = time.time()
424
+ round_counter = 1
425
+ target_content_pairs = []
426
+
427
+ try:
428
+ # Execute user's inference function, passing case data list
429
+ user_results = inference_function(case_data_list)
430
+ end_time = time.time()
431
+ inference_duration = int((end_time - start_time) * 1000)
432
+
433
+ # Wrap each result
434
+ for user_result in user_results:
435
+ target_content_pair = eva_types.EvaTaskResultTargetContentPair(
436
+ Content=user_result.Content,
437
+ ContentThought=user_result.ContentThought,
438
+ Round=round_counter,
439
+ MessageID=None, # Not needed
440
+ ConversationID=None, # Not needed
441
+ Status=eva_types.EvaConversationStatus.SUCCEED, # Success status
442
+ StatusMessage=None,
443
+ CostTokens=user_result.CostTokens,
444
+ InferenceDuration=inference_duration,
445
+ RuleDuration=None, # Not needed
446
+ TTFT=user_result.TTFT,
447
+ )
448
+ target_content_pairs.append(target_content_pair)
449
+ round_counter += 1
450
+
451
+ except Exception as e:
452
+ # If there's an exception, create a failed status result
453
+ end_time = time.time()
454
+ inference_duration = int((end_time - start_time) * 1000)
455
+
456
+ target_content_pair = eva_types.EvaTaskResultTargetContentPair(
457
+ Content=None,
458
+ ContentThought=None,
459
+ Round=1,
460
+ MessageID=None,
461
+ ConversationID=None,
462
+ Status=eva_types.EvaConversationStatus.FAILED,
463
+ StatusMessage=str(e), # Exception message as status message
464
+ CostTokens=None,
465
+ InferenceDuration=inference_duration,
466
+ RuleDuration=None,
467
+ TTFT=None,
468
+ )
469
+ target_content_pairs.append(target_content_pair)
470
+
471
+ self.logger.error(f"Inference failed for row {row_id}: {e}")
472
+
473
+ return target_content_pairs
474
+
475
+ def _wait_task_finished(self, task_id: str):
476
+ request = eva_types.GetEvaTaskRequest(
477
+ WorkspaceID=self.workspace_id, TaskID=task_id
478
+ )
479
+ task = self.eva_service.GetEvaTask(request)
480
+ while task.Status not in [
481
+ eva_types.EvaTaskStatus.SUCCEED,
482
+ eva_types.EvaTaskStatus.FAILED,
483
+ eva_types.EvaTaskStatus.CANCELLED,
484
+ eva_types.EvaTaskStatus.PARTIAL_SUCCEED,
485
+ ]:
486
+ time.sleep(1)
487
+ task = self.eva_service.GetEvaTask(request)
@@ -0,0 +1,14 @@
1
+ [project]
2
+ name = "hiagent-eva"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = []
8
+
9
+ [build-system]
10
+ requires = ["hatchling"]
11
+ build-backend = "hatchling.build"
12
+
13
+ [tool.hatch.build.targets.wheel]
14
+ packages = ["."]
@@ -0,0 +1,145 @@
1
+ # Copyright (c) 2024 Bytedance Ltd. and/or its affiliates
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #!/usr/bin/env python3
15
+ """
16
+ Eva SDK Usage Example
17
+
18
+ Demonstrates how to use Eva SDK to create and execute evaluation tasks
19
+ """
20
+
21
+ import argparse
22
+ import json
23
+ import logging
24
+ import os
25
+ from time import sleep
26
+ from typing import List
27
+
28
+ from dotenv import load_dotenv
29
+
30
+ # Import Eva SDK
31
+ from libs.api.hiagent_api import eva_types
32
+ from libs.eva import client
33
+
34
+ # Configure logging
35
+ logging.basicConfig(level=logging.INFO)
36
+ logger = logging.getLogger(__name__)
37
+
38
+ load_dotenv()
39
+
40
+
41
+ def my_inference_function(
42
+ case_data_list: List[eva_types.CaseData],
43
+ ) -> List[eva_types.InferenceResult]:
44
+ results = []
45
+ message_list = []
46
+ for case in case_data_list:
47
+ input = case["input"][0].Text
48
+ message_list.append({"role": "user", "content": input})
49
+ content = f"message list={json.dumps(message_list, ensure_ascii=False)}"
50
+ message_list.append({"role": "assistant", "content": content})
51
+ # Create inference result
52
+ result = eva_types.InferenceResult(
53
+ Content=content,
54
+ CostTokens=100,
55
+ TTFT=101,
56
+ )
57
+ results.append(result)
58
+ sleep(0.5)
59
+
60
+ return results
61
+
62
+
63
+ def main():
64
+ """Main function: Run complete evaluation example"""
65
+
66
+ # Parse command line arguments
67
+ parser = argparse.ArgumentParser(description="Eva SDK Evaluation Example")
68
+ parser.add_argument(
69
+ "-d", "--dataset-id", required=True, help="Dataset ID for evaluation"
70
+ )
71
+ parser.add_argument(
72
+ "-r", "--ruleset-id", required=True, help="Ruleset ID for evaluation"
73
+ )
74
+ parser.add_argument("-n", "--name", required=True, help="Task name")
75
+ args = parser.parse_args()
76
+
77
+ print("=== Eva SDK Example ===\n")
78
+
79
+ # Initialize client
80
+ provider = client.init(
81
+ endpoint=os.getenv("HIAGENT_TOP_ENDPOINT"),
82
+ ak=os.getenv("VOLC_ACCESSKEY"),
83
+ sk=os.getenv("VOLC_SECRETKEY"),
84
+ workspace_id=os.getenv("WORKSPACE_ID"),
85
+ app_id=os.getenv("CUSTOM_APP_ID"),
86
+ )
87
+
88
+ # Use command line arguments
89
+ dataset_id = args.dataset_id
90
+ ruleset_id = args.ruleset_id
91
+ task_name = args.name
92
+
93
+ try:
94
+ # Run evaluation
95
+ print("Starting evaluation...")
96
+ print(f"Dataset ID: {dataset_id}")
97
+ print(f"Ruleset ID: {ruleset_id}")
98
+ report = provider.run_evaluation(
99
+ dataset_id=dataset_id,
100
+ task_name=task_name,
101
+ inference_function=my_inference_function,
102
+ ruleset_id=ruleset_id,
103
+ max_conversations=5,
104
+ )
105
+
106
+ # Print results
107
+ print("\n✓ Evaluation completed!")
108
+ print(f" Task ID: {report.TaskID}")
109
+ print(f" Task Name: {report.TaskName}")
110
+ print(f" Status: {report.Status}")
111
+ print(f" Number of Rules: {len(report.Rules)}")
112
+ print(f" Number of Targets: {len(report.Targets)}")
113
+ print(f" Created At: {report.CreatedAt}")
114
+ print(f" Updated At: {report.UpdatedAt}")
115
+
116
+ # Display target information
117
+ if report.Targets:
118
+ print("\nInference Information:")
119
+ for target in report.Targets:
120
+ print(f" • Target ID: {target.TargetID}")
121
+ print(f" Target Name: {target.TargetDetail.TargetName}")
122
+ print(f" Average Token Cost: {target.AvgCostTokens}")
123
+ print(f" Average TTFT: {target.AvgTTFT}ms")
124
+ print(f" Average Duration: {target.AvgDuration}ms")
125
+ print(f" Total Token Cost: {target.CostTokens}")
126
+
127
+ # Display rule information
128
+ if report.Rules:
129
+ print("\nRule Information:")
130
+ for rule in report.Rules:
131
+ print(f" • Rule ID: {rule.RuleID}")
132
+ for rule_target in rule.Targets:
133
+ print(f" Target: {rule_target.TargetDetail.TargetName}")
134
+ print(f" Average Score: {rule_target.AvgScore}")
135
+ print(f" Percentage: {rule_target.Percent}%")
136
+
137
+ print("\n=== Evaluation Completed ===")
138
+
139
+ except Exception as e:
140
+ logger.error(f"Evaluation execution failed: {e}", exc_info=True)
141
+ print(f"✗ Evaluation execution failed: {e}")
142
+
143
+
144
+ if __name__ == "__main__":
145
+ main()