amd-gaia 0.15.0__py3-none-any.whl → 0.15.2__py3-none-any.whl
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.
- {amd_gaia-0.15.0.dist-info → amd_gaia-0.15.2.dist-info}/METADATA +222 -223
- amd_gaia-0.15.2.dist-info/RECORD +182 -0
- {amd_gaia-0.15.0.dist-info → amd_gaia-0.15.2.dist-info}/WHEEL +1 -1
- {amd_gaia-0.15.0.dist-info → amd_gaia-0.15.2.dist-info}/entry_points.txt +1 -0
- {amd_gaia-0.15.0.dist-info → amd_gaia-0.15.2.dist-info}/licenses/LICENSE.md +20 -20
- gaia/__init__.py +29 -29
- gaia/agents/__init__.py +19 -19
- gaia/agents/base/__init__.py +9 -9
- gaia/agents/base/agent.py +2132 -2177
- gaia/agents/base/api_agent.py +119 -120
- gaia/agents/base/console.py +1967 -1841
- gaia/agents/base/errors.py +237 -237
- gaia/agents/base/mcp_agent.py +86 -86
- gaia/agents/base/tools.py +88 -83
- gaia/agents/blender/__init__.py +7 -0
- gaia/agents/blender/agent.py +553 -556
- gaia/agents/blender/agent_simple.py +133 -135
- gaia/agents/blender/app.py +211 -211
- gaia/agents/blender/app_simple.py +41 -41
- gaia/agents/blender/core/__init__.py +16 -16
- gaia/agents/blender/core/materials.py +506 -506
- gaia/agents/blender/core/objects.py +316 -316
- gaia/agents/blender/core/rendering.py +225 -225
- gaia/agents/blender/core/scene.py +220 -220
- gaia/agents/blender/core/view.py +146 -146
- gaia/agents/chat/__init__.py +9 -9
- gaia/agents/chat/agent.py +809 -835
- gaia/agents/chat/app.py +1065 -1058
- gaia/agents/chat/session.py +508 -508
- gaia/agents/chat/tools/__init__.py +15 -15
- gaia/agents/chat/tools/file_tools.py +96 -96
- gaia/agents/chat/tools/rag_tools.py +1744 -1729
- gaia/agents/chat/tools/shell_tools.py +437 -436
- gaia/agents/code/__init__.py +7 -7
- gaia/agents/code/agent.py +549 -549
- gaia/agents/code/cli.py +377 -0
- gaia/agents/code/models.py +135 -135
- gaia/agents/code/orchestration/__init__.py +24 -24
- gaia/agents/code/orchestration/checklist_executor.py +1763 -1763
- gaia/agents/code/orchestration/checklist_generator.py +713 -713
- gaia/agents/code/orchestration/factories/__init__.py +9 -9
- gaia/agents/code/orchestration/factories/base.py +63 -63
- gaia/agents/code/orchestration/factories/nextjs_factory.py +118 -118
- gaia/agents/code/orchestration/factories/python_factory.py +106 -106
- gaia/agents/code/orchestration/orchestrator.py +841 -841
- gaia/agents/code/orchestration/project_analyzer.py +391 -391
- gaia/agents/code/orchestration/steps/__init__.py +67 -67
- gaia/agents/code/orchestration/steps/base.py +188 -188
- gaia/agents/code/orchestration/steps/error_handler.py +314 -314
- gaia/agents/code/orchestration/steps/nextjs.py +828 -828
- gaia/agents/code/orchestration/steps/python.py +307 -307
- gaia/agents/code/orchestration/template_catalog.py +469 -469
- gaia/agents/code/orchestration/workflows/__init__.py +14 -14
- gaia/agents/code/orchestration/workflows/base.py +80 -80
- gaia/agents/code/orchestration/workflows/nextjs.py +186 -186
- gaia/agents/code/orchestration/workflows/python.py +94 -94
- gaia/agents/code/prompts/__init__.py +11 -11
- gaia/agents/code/prompts/base_prompt.py +77 -77
- gaia/agents/code/prompts/code_patterns.py +2034 -2036
- gaia/agents/code/prompts/nextjs_prompt.py +40 -40
- gaia/agents/code/prompts/python_prompt.py +109 -109
- gaia/agents/code/schema_inference.py +365 -365
- gaia/agents/code/system_prompt.py +41 -41
- gaia/agents/code/tools/__init__.py +42 -42
- gaia/agents/code/tools/cli_tools.py +1138 -1138
- gaia/agents/code/tools/code_formatting.py +319 -319
- gaia/agents/code/tools/code_tools.py +769 -769
- gaia/agents/code/tools/error_fixing.py +1347 -1347
- gaia/agents/code/tools/external_tools.py +180 -180
- gaia/agents/code/tools/file_io.py +845 -845
- gaia/agents/code/tools/prisma_tools.py +190 -190
- gaia/agents/code/tools/project_management.py +1016 -1016
- gaia/agents/code/tools/testing.py +321 -321
- gaia/agents/code/tools/typescript_tools.py +122 -122
- gaia/agents/code/tools/validation_parsing.py +461 -461
- gaia/agents/code/tools/validation_tools.py +806 -806
- gaia/agents/code/tools/web_dev_tools.py +1758 -1758
- gaia/agents/code/validators/__init__.py +16 -16
- gaia/agents/code/validators/antipattern_checker.py +241 -241
- gaia/agents/code/validators/ast_analyzer.py +197 -197
- gaia/agents/code/validators/requirements_validator.py +145 -145
- gaia/agents/code/validators/syntax_validator.py +171 -171
- gaia/agents/docker/__init__.py +7 -7
- gaia/agents/docker/agent.py +643 -642
- gaia/agents/emr/__init__.py +8 -8
- gaia/agents/emr/agent.py +1504 -1506
- gaia/agents/emr/cli.py +1322 -1322
- gaia/agents/emr/constants.py +475 -475
- gaia/agents/emr/dashboard/__init__.py +4 -4
- gaia/agents/emr/dashboard/server.py +1972 -1974
- gaia/agents/jira/__init__.py +11 -11
- gaia/agents/jira/agent.py +894 -894
- gaia/agents/jira/jql_templates.py +299 -299
- gaia/agents/routing/__init__.py +7 -7
- gaia/agents/routing/agent.py +567 -570
- gaia/agents/routing/system_prompt.py +75 -75
- gaia/agents/summarize/__init__.py +11 -0
- gaia/agents/summarize/agent.py +885 -0
- gaia/agents/summarize/prompts.py +129 -0
- gaia/api/__init__.py +23 -23
- gaia/api/agent_registry.py +238 -238
- gaia/api/app.py +305 -305
- gaia/api/openai_server.py +575 -575
- gaia/api/schemas.py +186 -186
- gaia/api/sse_handler.py +373 -373
- gaia/apps/__init__.py +4 -4
- gaia/apps/llm/__init__.py +6 -6
- gaia/apps/llm/app.py +184 -169
- gaia/apps/summarize/app.py +116 -633
- gaia/apps/summarize/html_viewer.py +133 -133
- gaia/apps/summarize/pdf_formatter.py +284 -284
- gaia/audio/__init__.py +2 -2
- gaia/audio/audio_client.py +439 -439
- gaia/audio/audio_recorder.py +269 -269
- gaia/audio/kokoro_tts.py +599 -599
- gaia/audio/whisper_asr.py +432 -432
- gaia/chat/__init__.py +16 -16
- gaia/chat/app.py +428 -430
- gaia/chat/prompts.py +522 -522
- gaia/chat/sdk.py +1228 -1225
- gaia/cli.py +5659 -5632
- gaia/database/__init__.py +10 -10
- gaia/database/agent.py +176 -176
- gaia/database/mixin.py +290 -290
- gaia/database/testing.py +64 -64
- gaia/eval/batch_experiment.py +2332 -2332
- gaia/eval/claude.py +542 -542
- gaia/eval/config.py +37 -37
- gaia/eval/email_generator.py +512 -512
- gaia/eval/eval.py +3179 -3179
- gaia/eval/groundtruth.py +1130 -1130
- gaia/eval/transcript_generator.py +582 -582
- gaia/eval/webapp/README.md +167 -167
- gaia/eval/webapp/package-lock.json +875 -875
- gaia/eval/webapp/package.json +20 -20
- gaia/eval/webapp/public/app.js +3402 -3402
- gaia/eval/webapp/public/index.html +87 -87
- gaia/eval/webapp/public/styles.css +3661 -3661
- gaia/eval/webapp/server.js +415 -415
- gaia/eval/webapp/test-setup.js +72 -72
- gaia/installer/__init__.py +23 -0
- gaia/installer/init_command.py +1275 -0
- gaia/installer/lemonade_installer.py +619 -0
- gaia/llm/__init__.py +10 -2
- gaia/llm/base_client.py +60 -0
- gaia/llm/exceptions.py +12 -0
- gaia/llm/factory.py +70 -0
- gaia/llm/lemonade_client.py +3421 -3221
- gaia/llm/lemonade_manager.py +294 -294
- gaia/llm/providers/__init__.py +9 -0
- gaia/llm/providers/claude.py +108 -0
- gaia/llm/providers/lemonade.py +118 -0
- gaia/llm/providers/openai_provider.py +79 -0
- gaia/llm/vlm_client.py +382 -382
- gaia/logger.py +189 -189
- gaia/mcp/agent_mcp_server.py +245 -245
- gaia/mcp/blender_mcp_client.py +138 -138
- gaia/mcp/blender_mcp_server.py +648 -648
- gaia/mcp/context7_cache.py +332 -332
- gaia/mcp/external_services.py +518 -518
- gaia/mcp/mcp_bridge.py +811 -550
- gaia/mcp/servers/__init__.py +6 -6
- gaia/mcp/servers/docker_mcp.py +83 -83
- gaia/perf_analysis.py +361 -0
- gaia/rag/__init__.py +10 -10
- gaia/rag/app.py +293 -293
- gaia/rag/demo.py +304 -304
- gaia/rag/pdf_utils.py +235 -235
- gaia/rag/sdk.py +2194 -2194
- gaia/security.py +183 -163
- gaia/talk/app.py +287 -289
- gaia/talk/sdk.py +538 -538
- gaia/testing/__init__.py +87 -87
- gaia/testing/assertions.py +330 -330
- gaia/testing/fixtures.py +333 -333
- gaia/testing/mocks.py +493 -493
- gaia/util.py +46 -46
- gaia/utils/__init__.py +33 -33
- gaia/utils/file_watcher.py +675 -675
- gaia/utils/parsing.py +223 -223
- gaia/version.py +100 -100
- amd_gaia-0.15.0.dist-info/RECORD +0 -168
- gaia/agents/code/app.py +0 -266
- gaia/llm/llm_client.py +0 -723
- {amd_gaia-0.15.0.dist-info → amd_gaia-0.15.2.dist-info}/top_level.txt +0 -0
gaia/mcp/servers/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
|
|
4
|
-
"""
|
|
5
|
-
MCP Server Launchers for GAIA Agents
|
|
6
|
-
"""
|
|
1
|
+
# Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
MCP Server Launchers for GAIA Agents
|
|
6
|
+
"""
|
gaia/mcp/servers/docker_mcp.py
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
# Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
|
|
4
|
-
"""
|
|
5
|
-
Docker MCP Server Launcher
|
|
6
|
-
Starts an MCP server for the Docker agent
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from gaia.agents.docker.agent import DockerAgent
|
|
10
|
-
from gaia.mcp.agent_mcp_server import MCP_DEFAULT_HOST, MCP_DEFAULT_PORT, AgentMCPServer
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def start_docker_mcp(
|
|
14
|
-
port: int = None,
|
|
15
|
-
host: str = None,
|
|
16
|
-
verbose: bool = False,
|
|
17
|
-
model_id: str = None,
|
|
18
|
-
silent_mode: bool = True,
|
|
19
|
-
):
|
|
20
|
-
"""
|
|
21
|
-
Start the Docker MCP server.
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
port: Port to listen on (default: 8080)
|
|
25
|
-
host: Host to bind to (default: localhost)
|
|
26
|
-
verbose: Enable verbose logging
|
|
27
|
-
model_id: LLM model ID to use
|
|
28
|
-
silent_mode: Suppress agent console output (default: True for MCP)
|
|
29
|
-
"""
|
|
30
|
-
# Prepare agent parameters
|
|
31
|
-
agent_params = {
|
|
32
|
-
"silent_mode": silent_mode,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if model_id:
|
|
36
|
-
agent_params["model_id"] = model_id
|
|
37
|
-
|
|
38
|
-
# Create and start MCP server
|
|
39
|
-
server = AgentMCPServer(
|
|
40
|
-
agent_class=DockerAgent,
|
|
41
|
-
name="GAIA Docker MCP",
|
|
42
|
-
port=port or MCP_DEFAULT_PORT,
|
|
43
|
-
host=host or MCP_DEFAULT_HOST,
|
|
44
|
-
verbose=verbose,
|
|
45
|
-
agent_params=agent_params,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
server.start()
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if __name__ == "__main__":
|
|
52
|
-
import argparse
|
|
53
|
-
|
|
54
|
-
parser = argparse.ArgumentParser(description="GAIA Docker MCP Server")
|
|
55
|
-
parser.add_argument(
|
|
56
|
-
"--port",
|
|
57
|
-
type=int,
|
|
58
|
-
default=MCP_DEFAULT_PORT,
|
|
59
|
-
help=f"Port to listen on (default: {MCP_DEFAULT_PORT})",
|
|
60
|
-
)
|
|
61
|
-
parser.add_argument(
|
|
62
|
-
"--host",
|
|
63
|
-
default=MCP_DEFAULT_HOST,
|
|
64
|
-
help=f"Host to bind to (default: {MCP_DEFAULT_HOST})",
|
|
65
|
-
)
|
|
66
|
-
parser.add_argument(
|
|
67
|
-
"--verbose",
|
|
68
|
-
action="store_true",
|
|
69
|
-
help="Enable verbose logging",
|
|
70
|
-
)
|
|
71
|
-
parser.add_argument(
|
|
72
|
-
"--model-id",
|
|
73
|
-
help="LLM model ID to use (default: Qwen3-Coder-30B-A3B-Instruct-GGUF)",
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
args = parser.parse_args()
|
|
77
|
-
|
|
78
|
-
start_docker_mcp(
|
|
79
|
-
port=args.port,
|
|
80
|
-
host=args.host,
|
|
81
|
-
verbose=args.verbose,
|
|
82
|
-
model_id=args.model_id,
|
|
83
|
-
)
|
|
1
|
+
# Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Docker MCP Server Launcher
|
|
6
|
+
Starts an MCP server for the Docker agent
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from gaia.agents.docker.agent import DockerAgent
|
|
10
|
+
from gaia.mcp.agent_mcp_server import MCP_DEFAULT_HOST, MCP_DEFAULT_PORT, AgentMCPServer
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def start_docker_mcp(
|
|
14
|
+
port: int = None,
|
|
15
|
+
host: str = None,
|
|
16
|
+
verbose: bool = False,
|
|
17
|
+
model_id: str = None,
|
|
18
|
+
silent_mode: bool = True,
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
Start the Docker MCP server.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
port: Port to listen on (default: 8080)
|
|
25
|
+
host: Host to bind to (default: localhost)
|
|
26
|
+
verbose: Enable verbose logging
|
|
27
|
+
model_id: LLM model ID to use
|
|
28
|
+
silent_mode: Suppress agent console output (default: True for MCP)
|
|
29
|
+
"""
|
|
30
|
+
# Prepare agent parameters
|
|
31
|
+
agent_params = {
|
|
32
|
+
"silent_mode": silent_mode,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if model_id:
|
|
36
|
+
agent_params["model_id"] = model_id
|
|
37
|
+
|
|
38
|
+
# Create and start MCP server
|
|
39
|
+
server = AgentMCPServer(
|
|
40
|
+
agent_class=DockerAgent,
|
|
41
|
+
name="GAIA Docker MCP",
|
|
42
|
+
port=port or MCP_DEFAULT_PORT,
|
|
43
|
+
host=host or MCP_DEFAULT_HOST,
|
|
44
|
+
verbose=verbose,
|
|
45
|
+
agent_params=agent_params,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
server.start()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
import argparse
|
|
53
|
+
|
|
54
|
+
parser = argparse.ArgumentParser(description="GAIA Docker MCP Server")
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"--port",
|
|
57
|
+
type=int,
|
|
58
|
+
default=MCP_DEFAULT_PORT,
|
|
59
|
+
help=f"Port to listen on (default: {MCP_DEFAULT_PORT})",
|
|
60
|
+
)
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"--host",
|
|
63
|
+
default=MCP_DEFAULT_HOST,
|
|
64
|
+
help=f"Host to bind to (default: {MCP_DEFAULT_HOST})",
|
|
65
|
+
)
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
"--verbose",
|
|
68
|
+
action="store_true",
|
|
69
|
+
help="Enable verbose logging",
|
|
70
|
+
)
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"--model-id",
|
|
73
|
+
help="LLM model ID to use (default: Qwen3-Coder-30B-A3B-Instruct-GGUF)",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
args = parser.parse_args()
|
|
77
|
+
|
|
78
|
+
start_docker_mcp(
|
|
79
|
+
port=args.port,
|
|
80
|
+
host=args.host,
|
|
81
|
+
verbose=args.verbose,
|
|
82
|
+
model_id=args.model_id,
|
|
83
|
+
)
|
gaia/perf_analysis.py
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# Copyright(C) 2025-2026 Advanced Micro Devices, Inc. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import math
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Callable, Dict, Iterable, List, Sequence, Tuple
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
import matplotlib.pyplot as plt
|
|
17
|
+
except ImportError: # pragma: no cover - handled at runtime
|
|
18
|
+
plt = None # type: ignore[assignment]
|
|
19
|
+
|
|
20
|
+
PROMPT_MARKER = "new prompt"
|
|
21
|
+
TOKEN_PATTERN = re.compile(r"n_prompt_tokens\s*=\s*(\d+)")
|
|
22
|
+
INPUT_PATTERN = re.compile(r"Input tokens:\s*(\d+)", re.IGNORECASE)
|
|
23
|
+
OUTPUT_PATTERN = re.compile(r"Output tokens:\s*(\d+)", re.IGNORECASE)
|
|
24
|
+
TTFT_PATTERN = re.compile(r"TTFT\s*\(s\):\s*([0-9]*\.?[0-9]+)", re.IGNORECASE)
|
|
25
|
+
TPS_PATTERN = re.compile(r"TPS:\s*([0-9]*\.?[0-9]+)", re.IGNORECASE)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _require_matplotlib() -> Any:
|
|
29
|
+
if plt is None:
|
|
30
|
+
raise RuntimeError(
|
|
31
|
+
"matplotlib is required for perf-vis. "
|
|
32
|
+
"Install with `pip install matplotlib` or `pip install -e .[dev]`."
|
|
33
|
+
)
|
|
34
|
+
return plt
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Metric(str, Enum):
|
|
38
|
+
PROMPT_TOKENS = "prompt_tokens"
|
|
39
|
+
INPUT_TOKENS = "input_tokens"
|
|
40
|
+
OUTPUT_TOKENS = "output_tokens"
|
|
41
|
+
TTFT = "ttft"
|
|
42
|
+
TPS = "tps"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class MetricConfig:
|
|
47
|
+
title: str
|
|
48
|
+
y_label: str
|
|
49
|
+
filename: Path
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
METRIC_CONFIGS: Dict[Metric, MetricConfig] = {
|
|
53
|
+
Metric.PROMPT_TOKENS: MetricConfig(
|
|
54
|
+
title="Prompt token counts",
|
|
55
|
+
y_label="Prompt token count",
|
|
56
|
+
filename=Path("prompt_token_counts.png"),
|
|
57
|
+
),
|
|
58
|
+
Metric.INPUT_TOKENS: MetricConfig(
|
|
59
|
+
title="Input token counts",
|
|
60
|
+
y_label="Input token count",
|
|
61
|
+
filename=Path("input_token_counts.png"),
|
|
62
|
+
),
|
|
63
|
+
Metric.OUTPUT_TOKENS: MetricConfig(
|
|
64
|
+
title="Output token counts",
|
|
65
|
+
y_label="Output token count",
|
|
66
|
+
filename=Path("output_token_counts.png"),
|
|
67
|
+
),
|
|
68
|
+
Metric.TTFT: MetricConfig(
|
|
69
|
+
title="Time to first token (s)",
|
|
70
|
+
y_label="Seconds to first token",
|
|
71
|
+
filename=Path("ttft_seconds.png"),
|
|
72
|
+
),
|
|
73
|
+
Metric.TPS: MetricConfig(
|
|
74
|
+
title="Tokens per second",
|
|
75
|
+
y_label="Tokens per second",
|
|
76
|
+
filename=Path("tps.png"),
|
|
77
|
+
),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def extract_metrics(lines: Iterable[str]) -> Dict[Metric, List[float]]:
|
|
82
|
+
"""Collect telemetry values by metric."""
|
|
83
|
+
values: Dict[Metric, List[float]] = {metric: [] for metric in Metric}
|
|
84
|
+
all_lines = list(lines)
|
|
85
|
+
awaiting_prompt_token = False
|
|
86
|
+
|
|
87
|
+
for raw_line in all_lines:
|
|
88
|
+
line = raw_line.strip()
|
|
89
|
+
lower_line = line.lower()
|
|
90
|
+
|
|
91
|
+
if not awaiting_prompt_token and PROMPT_MARKER in lower_line:
|
|
92
|
+
awaiting_prompt_token = True
|
|
93
|
+
|
|
94
|
+
input_match = INPUT_PATTERN.search(line)
|
|
95
|
+
if input_match:
|
|
96
|
+
values[Metric.INPUT_TOKENS].append(float(input_match.group(1)))
|
|
97
|
+
|
|
98
|
+
if awaiting_prompt_token:
|
|
99
|
+
prompt_match = TOKEN_PATTERN.search(line)
|
|
100
|
+
if prompt_match:
|
|
101
|
+
values[Metric.PROMPT_TOKENS].append(float(prompt_match.group(1)))
|
|
102
|
+
awaiting_prompt_token = False
|
|
103
|
+
|
|
104
|
+
output_match = OUTPUT_PATTERN.search(line)
|
|
105
|
+
if output_match:
|
|
106
|
+
values[Metric.OUTPUT_TOKENS].append(float(output_match.group(1)))
|
|
107
|
+
|
|
108
|
+
ttft_match = TTFT_PATTERN.search(line)
|
|
109
|
+
if ttft_match:
|
|
110
|
+
values[Metric.TTFT].append(float(ttft_match.group(1)))
|
|
111
|
+
|
|
112
|
+
tps_match = TPS_PATTERN.search(line)
|
|
113
|
+
if tps_match:
|
|
114
|
+
values[Metric.TPS].append(float(tps_match.group(1)))
|
|
115
|
+
|
|
116
|
+
return values
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def build_plot(
|
|
120
|
+
series: Sequence[Tuple[str, List[float]]],
|
|
121
|
+
metric_config: MetricConfig,
|
|
122
|
+
output_path: Path,
|
|
123
|
+
show: bool,
|
|
124
|
+
) -> None:
|
|
125
|
+
"""Create the plot and either save it, display it, or both."""
|
|
126
|
+
plt_mod = _require_matplotlib()
|
|
127
|
+
fig, ax = plt_mod.subplots()
|
|
128
|
+
|
|
129
|
+
for log_name, metric_values in series:
|
|
130
|
+
x_values = range(1, len(metric_values) + 1)
|
|
131
|
+
ax.plot(x_values, metric_values, marker="o", linestyle="-", label=log_name)
|
|
132
|
+
|
|
133
|
+
ax.set_xlabel("LLM call/inference count")
|
|
134
|
+
ax.set_ylabel(metric_config.y_label)
|
|
135
|
+
title = metric_config.title
|
|
136
|
+
if len(series) == 1:
|
|
137
|
+
title = f"{title} - {series[0][0]}"
|
|
138
|
+
ax.set_title(title)
|
|
139
|
+
ax.grid(True, linestyle="--", alpha=0.4)
|
|
140
|
+
if len(series) > 1:
|
|
141
|
+
ax.legend(title="Log file")
|
|
142
|
+
fig.tight_layout()
|
|
143
|
+
|
|
144
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
145
|
+
fig.savefig(output_path)
|
|
146
|
+
|
|
147
|
+
if show:
|
|
148
|
+
plt_mod.show()
|
|
149
|
+
else:
|
|
150
|
+
plt_mod.close(fig)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def build_prefill_decode_pies(
|
|
154
|
+
prefill_decode_times: Sequence[Tuple[str, float, float]],
|
|
155
|
+
output_path: Path,
|
|
156
|
+
show: bool,
|
|
157
|
+
) -> None:
|
|
158
|
+
"""Plot per-log prefill vs decode time split as multiple pies."""
|
|
159
|
+
plt_mod = _require_matplotlib()
|
|
160
|
+
|
|
161
|
+
if not prefill_decode_times:
|
|
162
|
+
print(
|
|
163
|
+
"No timing data available to build prefill/decode split.", file=sys.stderr
|
|
164
|
+
)
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
slice_colors = ["#4c78a8", "#f58518"] # Prefill, decode
|
|
168
|
+
pie_outline_colors = plt_mod.get_cmap("tab10").colors
|
|
169
|
+
cols = min(len(prefill_decode_times), 3)
|
|
170
|
+
rows = math.ceil(len(prefill_decode_times) / cols)
|
|
171
|
+
|
|
172
|
+
def autopct_with_seconds(values: Sequence[float]) -> Callable[[float], str]:
|
|
173
|
+
def format_pct(pct: float) -> str:
|
|
174
|
+
seconds = pct / 100 * sum(values)
|
|
175
|
+
return f"{pct:.1f}%\n{seconds:.2f}s"
|
|
176
|
+
|
|
177
|
+
return format_pct
|
|
178
|
+
|
|
179
|
+
fig, axes = plt_mod.subplots(rows, cols, figsize=(4 * cols, 4 * rows))
|
|
180
|
+
axes_flat: List[plt_mod.Axes] = (
|
|
181
|
+
[axes]
|
|
182
|
+
if isinstance(axes, plt_mod.Axes)
|
|
183
|
+
else list(axes.ravel() if hasattr(axes, "ravel") else axes)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
legend_handles = []
|
|
187
|
+
|
|
188
|
+
for idx, (log_name, prefill_total, decode_total) in enumerate(prefill_decode_times):
|
|
189
|
+
total_time = prefill_total + decode_total
|
|
190
|
+
if total_time <= 0:
|
|
191
|
+
axes_flat[idx].axis("off")
|
|
192
|
+
continue
|
|
193
|
+
|
|
194
|
+
ax = axes_flat[idx]
|
|
195
|
+
sizes = [prefill_total, decode_total]
|
|
196
|
+
outline_color = pie_outline_colors[idx % len(pie_outline_colors)]
|
|
197
|
+
|
|
198
|
+
ax.pie(
|
|
199
|
+
sizes,
|
|
200
|
+
labels=["Prefill", "Decode"],
|
|
201
|
+
colors=slice_colors,
|
|
202
|
+
autopct=autopct_with_seconds(sizes),
|
|
203
|
+
startangle=90,
|
|
204
|
+
wedgeprops={"edgecolor": outline_color, "linewidth": 2},
|
|
205
|
+
)
|
|
206
|
+
ax.axis("equal") # Equal aspect ratio for a true circle.
|
|
207
|
+
ax.set_title(log_name)
|
|
208
|
+
ax.text(
|
|
209
|
+
0,
|
|
210
|
+
0,
|
|
211
|
+
f"Total\n{total_time:.2f}s",
|
|
212
|
+
ha="center",
|
|
213
|
+
va="center",
|
|
214
|
+
fontsize=10,
|
|
215
|
+
weight="bold",
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
legend_handles.append(
|
|
219
|
+
plt_mod.Line2D(
|
|
220
|
+
[0],
|
|
221
|
+
[0],
|
|
222
|
+
marker="o",
|
|
223
|
+
color="w",
|
|
224
|
+
markerfacecolor="w",
|
|
225
|
+
markeredgecolor=outline_color,
|
|
226
|
+
markeredgewidth=2,
|
|
227
|
+
markersize=10,
|
|
228
|
+
label=log_name,
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
for ax in axes_flat[len(prefill_decode_times) :]:
|
|
233
|
+
ax.axis("off")
|
|
234
|
+
|
|
235
|
+
fig.legend(
|
|
236
|
+
handles=legend_handles,
|
|
237
|
+
loc="upper center",
|
|
238
|
+
ncol=min(len(legend_handles), cols),
|
|
239
|
+
title="Log file",
|
|
240
|
+
)
|
|
241
|
+
fig.suptitle("Prefill vs decode time split", y=0.98)
|
|
242
|
+
fig.tight_layout(rect=(0, 0, 1, 0.92))
|
|
243
|
+
|
|
244
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
245
|
+
fig.savefig(output_path)
|
|
246
|
+
|
|
247
|
+
if show:
|
|
248
|
+
plt_mod.show()
|
|
249
|
+
else:
|
|
250
|
+
plt_mod.close(fig)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def parse_cli(argv: Sequence[str] | None = None) -> argparse.Namespace:
|
|
254
|
+
parser = argparse.ArgumentParser(
|
|
255
|
+
description=(
|
|
256
|
+
"Plot telemetry values (prompt/input/output tokens, TTFT, TPS) "
|
|
257
|
+
"recorded in one or more llama.cpp server logs."
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
parser.add_argument(
|
|
261
|
+
"log_paths",
|
|
262
|
+
type=Path,
|
|
263
|
+
nargs="+",
|
|
264
|
+
help="One or more paths to llama.cpp server log files.",
|
|
265
|
+
)
|
|
266
|
+
parser.add_argument(
|
|
267
|
+
"--show",
|
|
268
|
+
action="store_true",
|
|
269
|
+
help="Display the plot window in addition to saving the image.",
|
|
270
|
+
)
|
|
271
|
+
return parser.parse_args(argv)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def run_perf_visualization(log_paths: Sequence[Path], show: bool = False) -> int:
|
|
275
|
+
"""Process log files and generate performance plots."""
|
|
276
|
+
try:
|
|
277
|
+
_ = _require_matplotlib()
|
|
278
|
+
except RuntimeError as exc:
|
|
279
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
280
|
+
return 1
|
|
281
|
+
|
|
282
|
+
metrics = list(Metric)
|
|
283
|
+
series_by_metric: Dict[Metric, List[Tuple[str, List[float]]]] = {
|
|
284
|
+
metric: [] for metric in metrics
|
|
285
|
+
}
|
|
286
|
+
prefill_decode_times: List[Tuple[str, float, float]] = []
|
|
287
|
+
|
|
288
|
+
for log_path in log_paths:
|
|
289
|
+
if not log_path.is_file():
|
|
290
|
+
print(f"error: log file not found: {log_path}", file=sys.stderr)
|
|
291
|
+
return 1
|
|
292
|
+
|
|
293
|
+
try:
|
|
294
|
+
with log_path.open("r", encoding="utf-8", errors="replace") as fh:
|
|
295
|
+
metric_values = extract_metrics(fh)
|
|
296
|
+
except OSError as exc:
|
|
297
|
+
print(f"error: failed to read log file {log_path}: {exc}", file=sys.stderr)
|
|
298
|
+
return 1
|
|
299
|
+
|
|
300
|
+
for metric in metrics:
|
|
301
|
+
values = metric_values.get(metric, [])
|
|
302
|
+
if not values:
|
|
303
|
+
print(
|
|
304
|
+
f"No {METRIC_CONFIGS[metric].title.lower()} were found in the log: "
|
|
305
|
+
f"{log_path}",
|
|
306
|
+
file=sys.stderr,
|
|
307
|
+
)
|
|
308
|
+
return 1
|
|
309
|
+
|
|
310
|
+
log_name = log_path.name or str(log_path)
|
|
311
|
+
series_by_metric[metric].append((log_name, values))
|
|
312
|
+
|
|
313
|
+
prefill_total_time = sum(metric_values[Metric.TTFT])
|
|
314
|
+
decode_total_time = sum(
|
|
315
|
+
output_tokens / tps
|
|
316
|
+
for output_tokens, tps in zip(
|
|
317
|
+
metric_values[Metric.OUTPUT_TOKENS], metric_values[Metric.TPS]
|
|
318
|
+
)
|
|
319
|
+
if tps > 0
|
|
320
|
+
)
|
|
321
|
+
prefill_decode_times.append(
|
|
322
|
+
(log_path.name or str(log_path), prefill_total_time, decode_total_time)
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
for metric in metrics:
|
|
326
|
+
build_plot(
|
|
327
|
+
series_by_metric[metric],
|
|
328
|
+
metric_config=METRIC_CONFIGS[metric],
|
|
329
|
+
output_path=METRIC_CONFIGS[metric].filename,
|
|
330
|
+
show=show,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
total_points = sum(len(counts) for _, counts in series_by_metric[metric])
|
|
334
|
+
print(
|
|
335
|
+
f"Saved {METRIC_CONFIGS[metric].title.lower()} plot with "
|
|
336
|
+
f"{total_points} entries from {len(series_by_metric[metric])} log(s) "
|
|
337
|
+
f"to {METRIC_CONFIGS[metric].filename}"
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
prefill_decode_path = Path("prefill_decode_split.png")
|
|
341
|
+
build_prefill_decode_pies(
|
|
342
|
+
prefill_decode_times=prefill_decode_times,
|
|
343
|
+
output_path=prefill_decode_path,
|
|
344
|
+
show=show,
|
|
345
|
+
)
|
|
346
|
+
if prefill_decode_times:
|
|
347
|
+
print(
|
|
348
|
+
f"Saved prefill vs decode time split pies for "
|
|
349
|
+
f"{len(prefill_decode_times)} log(s) to {prefill_decode_path}"
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
return 0
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
356
|
+
args = parse_cli(argv)
|
|
357
|
+
return run_perf_visualization(args.log_paths, show=args.show)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
if __name__ == "__main__":
|
|
361
|
+
raise SystemExit(main())
|
gaia/rag/__init__.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# Copyright(C) 2024-2025 Advanced Micro Devices, Inc. All rights reserved.
|
|
3
|
-
# SPDX-License-Identifier: MIT
|
|
4
|
-
|
|
5
|
-
"""GAIA RAG (Retrieval-Augmented Generation) Module"""
|
|
6
|
-
|
|
7
|
-
from .app import main as rag_main
|
|
8
|
-
from .sdk import RAGSDK, RAGConfig, quick_rag
|
|
9
|
-
|
|
10
|
-
__all__ = ["RAGConfig", "RAGSDK", "quick_rag", "rag_main"]
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright(C) 2024-2025 Advanced Micro Devices, Inc. All rights reserved.
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
"""GAIA RAG (Retrieval-Augmented Generation) Module"""
|
|
6
|
+
|
|
7
|
+
from .app import main as rag_main
|
|
8
|
+
from .sdk import RAGSDK, RAGConfig, quick_rag
|
|
9
|
+
|
|
10
|
+
__all__ = ["RAGConfig", "RAGSDK", "quick_rag", "rag_main"]
|