cmdbox 0.5.4__py3-none-any.whl → 0.6.0__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.
Potentially problematic release.
This version of cmdbox might be problematic. Click here for more details.
- cmdbox/app/auth/signin.py +463 -303
- cmdbox/app/common.py +48 -3
- cmdbox/app/edge.py +5 -173
- cmdbox/app/edge_tool.py +177 -0
- cmdbox/app/feature.py +10 -9
- cmdbox/app/features/cli/agent_base.py +477 -0
- cmdbox/app/features/cli/audit_base.py +1 -1
- cmdbox/app/features/cli/cmdbox_audit_search.py +24 -1
- cmdbox/app/features/cli/cmdbox_client_file_download.py +1 -1
- cmdbox/app/features/cli/cmdbox_cmd_list.py +105 -0
- cmdbox/app/features/cli/cmdbox_cmd_load.py +104 -0
- cmdbox/app/features/cli/cmdbox_edge_config.py +2 -2
- cmdbox/app/features/cli/cmdbox_edge_start.py +1 -1
- cmdbox/app/features/cli/cmdbox_gui_start.py +9 -132
- cmdbox/app/features/cli/cmdbox_gui_stop.py +4 -21
- cmdbox/app/features/cli/cmdbox_server_start.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_apikey_add.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_apikey_del.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_genpass.py +0 -3
- cmdbox/app/features/cli/cmdbox_web_group_add.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_group_del.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_group_edit.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_group_list.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_start.py +119 -104
- cmdbox/app/features/cli/cmdbox_web_stop.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_add.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_del.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_edit.py +1 -1
- cmdbox/app/features/cli/cmdbox_web_user_list.py +1 -1
- cmdbox/app/features/web/cmdbox_web_agent.py +250 -0
- cmdbox/app/features/web/cmdbox_web_exec_cmd.py +8 -3
- cmdbox/app/features/web/cmdbox_web_signin.py +3 -3
- cmdbox/app/features/web/cmdbox_web_users.py +2 -0
- cmdbox/app/options.py +55 -2
- cmdbox/app/web.py +142 -15
- cmdbox/extensions/features.yml +18 -0
- cmdbox/extensions/sample_project/sample/app/features/cli/__init__.py +0 -0
- cmdbox/extensions/sample_project/sample/app/features/web/__init__.py +0 -0
- cmdbox/extensions/user_list.yml +1 -0
- cmdbox/licenses/LICENSE.Authlib.1.5.2(BSD License).txt +29 -0
- cmdbox/licenses/LICENSE.Deprecated.1.2.18(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.SQLAlchemy.2.0.40(MIT License).txt +19 -0
- cmdbox/licenses/LICENSE.aiohappyeyeballs.2.6.1(Python Software Foundation License).txt +279 -0
- cmdbox/licenses/LICENSE.aiohttp.3.11.18(Apache Software License).txt +13 -0
- cmdbox/licenses/LICENSE.aiosignal.1.3.2(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.attrs.25.3.0(UNKNOWN).txt +21 -0
- cmdbox/licenses/LICENSE.cachetools.5.5.2(MIT License).txt +20 -0
- cmdbox/licenses/LICENSE.distro.1.9.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.docstring_parser.0.16(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.filelock.3.18.0(The Unlicense (Unlicense)).txt +24 -0
- cmdbox/licenses/LICENSE.frozenlist.1.6.0(Apache-2.0).txt +201 -0
- cmdbox/licenses/LICENSE.fsspec.2025.3.2(BSD License).txt +29 -0
- cmdbox/licenses/LICENSE.google-adk.0.5.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-api-python-client.2.169.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.google-auth-httplib2.0.2.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.google-auth.2.40.1(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.google-cloud-aiplatform.1.92.0(Apache 2.0).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-bigquery.3.31.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-core.2.4.3(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-resource-manager.1.14.2(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-secret-manager.2.23.3(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-speech.2.32.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-storage.2.19.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-cloud-trace.1.16.1(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-crc32c.1.7.1(Apache 2.0).txt +202 -0
- cmdbox/licenses/LICENSE.google-genai.1.14.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.google-resumable-media.2.7.2(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.googleapis-common-protos.1.70.0(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.graphviz.0.20.3(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.grpc-google-iam-v1.0.14.2(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.grpcio-status.1.71.0(Apache Software License).txt +610 -0
- cmdbox/licenses/LICENSE.grpcio.1.71.0(Apache Software License).txt +610 -0
- cmdbox/licenses/LICENSE.httpcore.1.0.9(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.httplib2.0.22.0(MIT License).txt +23 -0
- cmdbox/licenses/LICENSE.httpx-sse.0.4.0(MIT).txt +21 -0
- cmdbox/licenses/LICENSE.httpx.0.28.1(BSD License).txt +12 -0
- cmdbox/licenses/LICENSE.huggingface-hub.0.31.1(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.importlib_metadata.8.6.1(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.jiter.0.9.0(MIT License).txt +1 -0
- cmdbox/licenses/LICENSE.jsonschema-specifications.2025.4.1(UNKNOWN).txt +19 -0
- cmdbox/licenses/LICENSE.jsonschema.4.23.0(MIT License).txt +19 -0
- cmdbox/licenses/LICENSE.litellm.1.69.0(MIT License).txt +26 -0
- cmdbox/licenses/LICENSE.mcp.1.8.0(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.multidict.6.4.3(Apache Software License).txt +13 -0
- cmdbox/licenses/LICENSE.openai.1.75.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-api.1.33.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-exporter-gcp-trace.1.9.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-resourcedetector-gcp.1.9.0a0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-sdk.1.33.0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.opentelemetry-semantic-conventions.0.54b0(Apache Software License).txt +201 -0
- cmdbox/licenses/LICENSE.propcache.0.3.1(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.proto-plus.1.26.1(Apache Software License).txt +202 -0
- cmdbox/licenses/LICENSE.protobuf.5.29.4(3-Clause BSD License).txt +32 -0
- cmdbox/licenses/LICENSE.pyasn1.0.6.1(BSD License).txt +24 -0
- cmdbox/licenses/LICENSE.pyasn1_modules.0.4.2(BSD License).txt +24 -0
- cmdbox/licenses/LICENSE.pydantic-settings.2.9.1(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.pyparsing.3.2.3(MIT License).txt +18 -0
- cmdbox/licenses/LICENSE.python-dateutil.2.9.0.post0(Apache Software License; BSD License).txt +54 -0
- cmdbox/licenses/LICENSE.referencing.0.36.2(UNKNOWN).txt +19 -0
- cmdbox/licenses/LICENSE.regex.2024.11.6(Apache Software License).txt +208 -0
- cmdbox/licenses/LICENSE.rpds-py.0.24.0(MIT).txt +19 -0
- cmdbox/licenses/LICENSE.rsa.4.9.1(Apache Software License).txt +13 -0
- cmdbox/licenses/LICENSE.shapely.2.1.0(BSD License).txt +29 -0
- cmdbox/licenses/LICENSE.sse-starlette.2.3.4(BSD License).txt +27 -0
- cmdbox/licenses/LICENSE.tiktoken.0.9.0(MIT License).txt +21 -0
- cmdbox/licenses/LICENSE.tokenizers.0.21.1(Apache Software License).txt +1 -0
- cmdbox/licenses/LICENSE.tqdm.4.67.1(MIT License; Mozilla Public License 2.0 (MPL 2.0)).txt +49 -0
- cmdbox/licenses/LICENSE.tzlocal.5.3.1(MIT License).txt +19 -0
- cmdbox/licenses/LICENSE.uritemplate.4.1.1(Apache Software License; BSD License).txt +3 -0
- cmdbox/licenses/LICENSE.wrapt.1.17.2(BSD License).txt +24 -0
- cmdbox/licenses/LICENSE.yarl.1.20.0(Apache Software License).txt +202 -0
- cmdbox/licenses/files.txt +104 -11
- cmdbox/logconf_agent.yml +38 -0
- cmdbox/logconf_audit.yml +13 -5
- cmdbox/logconf_client.yml +13 -5
- cmdbox/logconf_cmdbox.yml +13 -5
- cmdbox/logconf_edge.yml +13 -5
- cmdbox/logconf_gui.yml +13 -5
- cmdbox/logconf_server.yml +13 -5
- cmdbox/logconf_web.yml +13 -5
- cmdbox/version.py +3 -2
- cmdbox/web/agent.html +263 -0
- cmdbox/web/assets/cmdbox/agent.js +335 -0
- cmdbox/web/assets/cmdbox/common.js +1111 -1020
- cmdbox/web/assets/cmdbox/signin.js +4 -4
- cmdbox/web/assets/filer/filer.js +4 -2
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.dist-info}/METADATA +69 -26
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.dist-info}/RECORD +143 -61
- /cmdbox/licenses/{LICENSE.charset-normalizer.3.4.1(MIT License).txt → LICENSE.charset-normalizer.3.4.2(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.click.8.1.8(BSD License).txt → LICENSE.click.8.2.0(UNKNOWN).txt} +0 -0
- /cmdbox/licenses/{LICENSE.cryptography.44.0.2(Apache Software License; BSD License).txt → LICENSE.cryptography.44.0.3(Apache Software License; BSD License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.importlib_metadata.8.7.0(Apache Software License).txt → LICENSE.google-api-core.2.24.2(Apache Software License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.greenlet.3.2.1(MIT AND Python-2.0).txt → LICENSE.greenlet.3.2.2(MIT AND Python-2.0).txt} +0 -0
- /cmdbox/licenses/{LICENSE.psycopg-binary.3.2.6(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE.psycopg-binary.3.2.7(GNU Lesser General Public License v3 (LGPLv3)).txt} +0 -0
- /cmdbox/licenses/{LICENSE.psycopg.3.2.6(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE.psycopg.3.2.7(GNU Lesser General Public License v3 (LGPLv3)).txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic.2.11.3(MIT License).txt → LICENSE.pydantic.2.11.4(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic_core.2.33.1(MIT License).txt → LICENSE.pydantic_core.2.33.2(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.redis.5.2.1(MIT License).txt → LICENSE.redis.6.0.0(MIT License).txt} +0 -0
- /cmdbox/licenses/{LICENSE.snowballstemmer.2.2.0(BSD License).txt → LICENSE.snowballstemmer.3.0.1(BSD License).txt} +0 -0
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.dist-info}/LICENSE +0 -0
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.dist-info}/WHEEL +0 -0
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.dist-info}/entry_points.txt +0 -0
- {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
from cmdbox.app import common, feature
|
|
2
|
+
from cmdbox.app.auth import signin
|
|
3
|
+
from cmdbox.app.features.cli import cmdbox_cmd_list
|
|
4
|
+
from cmdbox.app.options import Options
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Callable, Dict, Tuple, List
|
|
7
|
+
import argparse
|
|
8
|
+
import asyncio
|
|
9
|
+
import locale
|
|
10
|
+
import logging
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
import time
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AgentBase(feature.ResultEdgeFeature):
|
|
17
|
+
|
|
18
|
+
def get_option(self):
|
|
19
|
+
"""
|
|
20
|
+
この機能のオプションを返します
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Dict[str, Any]: オプション
|
|
24
|
+
"""
|
|
25
|
+
return dict(
|
|
26
|
+
use_redis=self.USE_REDIS_FALSE, nouse_webmode=False, use_agent=True,
|
|
27
|
+
discription_ja="-",
|
|
28
|
+
discription_en="-",
|
|
29
|
+
choice=[
|
|
30
|
+
dict(opt="agent", type=Options.T_STR, default="no", required=False, multi=False, hide=False, choice=["no", "use"],
|
|
31
|
+
discription_ja="エージェントを使用するかどうかを指定します。",
|
|
32
|
+
discription_en="Specifies whether the agent is used.",
|
|
33
|
+
choice_show=dict(use=["agent_name", "agent_description", "agent_instruction", "agent_session_store", "llmprov"],)),
|
|
34
|
+
dict(opt="agent_name", type=Options.T_STR, default=self.ver.__appid__, required=False, multi=False, hide=False, choice=None,
|
|
35
|
+
discription_ja="エージェント名を指定します。",
|
|
36
|
+
discription_en="Specifies the agent name."),
|
|
37
|
+
dict(opt="agent_description", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
38
|
+
discription_ja="エージェントの説明を指定します。",
|
|
39
|
+
discription_en="Specify agent description."),
|
|
40
|
+
dict(opt="agent_instruction", type=Options.T_TEXT, default=None, required=False, multi=False, hide=False, choice=None,
|
|
41
|
+
discription_ja="エージェントのシステム指示を指定します。",
|
|
42
|
+
discription_en="Specifies the agent's system instructions."),
|
|
43
|
+
dict(opt="agent_session_store", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=['memory', 'sqlite', 'postgresql'],
|
|
44
|
+
discription_ja="エージェントのセッションを保存する方法を指定します。",
|
|
45
|
+
discription_en="Specify how the agent's session is to be saved.",
|
|
46
|
+
choice_show=dict(postgresql=["pg_host", "pg_port", "pg_user", "pg_password", "pg_dbname"]),),
|
|
47
|
+
dict(opt="mcp_listen_port", type=Options.T_INT, default="9081", required=False, multi=False, hide=False, choice=None,
|
|
48
|
+
discription_ja="省略した時は `9081` を使用します。",
|
|
49
|
+
discription_en="If omitted, `9081` is used."),
|
|
50
|
+
dict(opt="mcp_ssl_listen_port", type=Options.T_INT, default="9443", required=False, multi=False, hide=False, choice=None,
|
|
51
|
+
discription_ja="省略した時は `9443` を使用します。",
|
|
52
|
+
discription_en="If omitted, `9443` is used."),
|
|
53
|
+
dict(opt="pg_host", type=Options.T_STR, default='localhost', required=False, multi=False, hide=True, choice=None, web="mask",
|
|
54
|
+
discription_ja="postgresqlホストを指定する。",
|
|
55
|
+
discription_en="Specify the postgresql host."),
|
|
56
|
+
dict(opt="pg_port", type=Options.T_INT, default=5432, required=False, multi=False, hide=True, choice=None, web="mask",
|
|
57
|
+
discription_ja="postgresqlのポートを指定する。",
|
|
58
|
+
discription_en="Specify the postgresql port."),
|
|
59
|
+
dict(opt="pg_user", type=Options.T_STR, default='postgres', required=False, multi=False, hide=True, choice=None, web="mask",
|
|
60
|
+
discription_ja="postgresqlのユーザー名を指定する。",
|
|
61
|
+
discription_en="Specify the postgresql user name."),
|
|
62
|
+
dict(opt="pg_password", type=Options.T_STR, default='postgres', required=False, multi=False, hide=True, choice=None, web="mask",
|
|
63
|
+
discription_ja="postgresqlのパスワードを指定する。",
|
|
64
|
+
discription_en="Specify the postgresql password."),
|
|
65
|
+
dict(opt="pg_dbname", type=Options.T_STR, default='agent', required=False, multi=False, hide=True, choice=None,
|
|
66
|
+
discription_ja="postgresqlデータベース名を指定します。",
|
|
67
|
+
discription_en="Specify the postgresql database name."),
|
|
68
|
+
dict(opt="llmprov", type=Options.T_STR, default=None, required=False, multi=False, hide=False,
|
|
69
|
+
choice=["", "azureopenai", "openai", "vertexai", "ollama"],
|
|
70
|
+
discription_ja="llmのプロバイダを指定します。",
|
|
71
|
+
discription_en="Specify llm provider.",
|
|
72
|
+
choice_show=dict(azureopenai=["llmapikey", "llmendpoint", "llmmodel", "llmapiversion"],
|
|
73
|
+
openai=["llmapikey", "llmendpoint", "llmmodel"],
|
|
74
|
+
vertexai=["llmprojectid", "llmsvaccountfile", "llmlocation", "llmmodel", "llmseed", "llmtemperature"],
|
|
75
|
+
ollama=["llmendpoint", "llmmodel", "llmtemperature"],),
|
|
76
|
+
),
|
|
77
|
+
dict(opt="llmprojectid", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
78
|
+
discription_ja="llmのプロバイダ接続のためのプロジェクトIDを指定します。",
|
|
79
|
+
discription_en="Specify the project ID for llm's provider connection."),
|
|
80
|
+
dict(opt="llmsvaccountfile", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
|
|
81
|
+
discription_ja="llmのプロバイダ接続のためのサービスアカウントファイルを指定します。",
|
|
82
|
+
discription_en="Specifies the service account file for llm's provider connection."),
|
|
83
|
+
dict(opt="llmlocation", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
84
|
+
discription_ja="llmのプロバイダ接続のためのロケーションを指定します。",
|
|
85
|
+
discription_en="Specifies the location for llm provider connections."),
|
|
86
|
+
dict(opt="llmapikey", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
87
|
+
discription_ja="llmのプロバイダ接続のためのAPIキーを指定します。",
|
|
88
|
+
discription_en="Specify API key for llm provider connection."),
|
|
89
|
+
dict(opt="llmapiversion", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
90
|
+
discription_ja="llmのプロバイダ接続のためのAPIバージョンを指定します。",
|
|
91
|
+
discription_en="Specifies the API version for llm provider connections."),
|
|
92
|
+
dict(opt="llmendpoint", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
|
|
93
|
+
discription_ja="llmのプロバイダ接続のためのエンドポイントを指定します。",
|
|
94
|
+
discription_en="Specifies the endpoint for llm provider connections."),
|
|
95
|
+
dict(opt="llmmodel", type=Options.T_STR, default="text-multilingual-embedding-002", required=False, multi=False, hide=False, choice=None,
|
|
96
|
+
discription_ja="llmモデルを指定します。",
|
|
97
|
+
discription_en="Specifies the llm model."),
|
|
98
|
+
dict(opt="llmseed", type=Options.T_INT, default=13, required=False, multi=False, hide=False, choice=None,
|
|
99
|
+
discription_ja="llmモデルを使用するときのシード値を指定します。",
|
|
100
|
+
discription_en="Specifies the seed value when using llm model."),
|
|
101
|
+
dict(opt="llmtemperature", type=Options.T_FLOAT, default=0.1, required=False, multi=False, hide=False, choice=None,
|
|
102
|
+
discription_ja="llmのモデルを使用するときのtemperatureを指定します。",
|
|
103
|
+
discription_en="Specifies the temperature when using llm model."),
|
|
104
|
+
])
|
|
105
|
+
|
|
106
|
+
def create_mcpserver(self, args:argparse.Namespace) -> Any:
|
|
107
|
+
"""
|
|
108
|
+
mcpserverを作成します
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
args (argparse.Namespace): 引数
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Any: FastMCP
|
|
115
|
+
"""
|
|
116
|
+
from mcp.server.fastmcp import FastMCP
|
|
117
|
+
mcp = FastMCP(name=self.ver.__appid__)
|
|
118
|
+
return mcp
|
|
119
|
+
|
|
120
|
+
def create_session_service(self, args:argparse.Namespace) -> Any:
|
|
121
|
+
"""
|
|
122
|
+
セッションサービスを作成します
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
args (argparse.Namespace): 引数
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
BaseSessionService: セッションサービス
|
|
129
|
+
"""
|
|
130
|
+
from google.adk.events import Event
|
|
131
|
+
from google.adk.sessions import DatabaseSessionService, InMemorySessionService, session
|
|
132
|
+
from typing_extensions import override
|
|
133
|
+
if hasattr(args, 'agent_session_dburl') and args.agent_session_dburl is not None:
|
|
134
|
+
class _DatabaseSessionService(DatabaseSessionService):
|
|
135
|
+
@override
|
|
136
|
+
async def append_event(self, session: session.Session, event: Event) -> Event:
|
|
137
|
+
# 永続化されるセッションには <important> タグを含めない
|
|
138
|
+
bk_parts = event.content.parts.copy()
|
|
139
|
+
for part in event.content.parts:
|
|
140
|
+
if not part.text: continue
|
|
141
|
+
part.text = re.sub(r"<important>.*</important>", "", part.text)
|
|
142
|
+
for part in bk_parts:
|
|
143
|
+
if not part.text: continue
|
|
144
|
+
part.text = part.text.replace("<important>", "").replace("</important>", "")
|
|
145
|
+
ret = await super().append_event(session, event)
|
|
146
|
+
ret.content.parts = bk_parts
|
|
147
|
+
return ret
|
|
148
|
+
dss = _DatabaseSessionService(db_url=args.agent_session_dburl)
|
|
149
|
+
#dss.db_engine.echo = True
|
|
150
|
+
return dss
|
|
151
|
+
else:
|
|
152
|
+
return InMemorySessionService()
|
|
153
|
+
|
|
154
|
+
def create_agent(self, logger:logging.Logger, args:argparse.Namespace, tools:List[Callable]) -> Any:
|
|
155
|
+
"""
|
|
156
|
+
エージェントを作成します
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
logger (logging.Logger): ロガー
|
|
160
|
+
args (argparse.Namespace): 引数
|
|
161
|
+
tools (List[Callable]): 関数
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Agent: エージェント
|
|
165
|
+
"""
|
|
166
|
+
if logger.level == logging.DEBUG:
|
|
167
|
+
logger.debug(f"create_agent processing..")
|
|
168
|
+
language, _ = locale.getlocale()
|
|
169
|
+
is_japan = language.find('Japan') >= 0 or language.find('ja_JP') >= 0
|
|
170
|
+
description = f"{self.ver.__appid__}に登録されているコマンド提供"
|
|
171
|
+
instruction = f"あなたはコマンドの意味を熟知しているエキスパートです。" + \
|
|
172
|
+
f"ユーザーがコマンドを実行したいとき、あなたは以下の手順に従ってコマンドを確実に実行してください。\n" + \
|
|
173
|
+
f"1. ユーザーのクエリからが実行したいコマンドを特定します。\n" + \
|
|
174
|
+
f"2. コマンド実行に必要なパラメータのなかで、ユーザーのクエリから取得できないものは、コマンド定義にあるデフォルト値を指定して実行してください。\n" + \
|
|
175
|
+
f"3. もしエラーが発生した場合は、ユーザーにコマンド名とパラメータとエラー内容を提示してください。\n"
|
|
176
|
+
"""
|
|
177
|
+
f"2. コマンド実行に必要なパラメータを特定します。\n" + \
|
|
178
|
+
f"3. ユーザーのクエリから指定しているパラメータを取得します。\n" + \
|
|
179
|
+
f"4. ユーザーが指定しているパラメータと、コマンド実行に必要なパラメータを比較し、不足しているパラメータを取得します。\n" + \
|
|
180
|
+
f"5. 以下に「パラメータ = デフォルト値」を示しているので、不足しているパラメータはデフォルト値を使用します。\n" + \
|
|
181
|
+
f" 但しコマンドが実行に必要のないパラメータは指定しないようにしてください。\n" + \
|
|
182
|
+
f" host = {args.host if hasattr(args, 'host') and args.host else self.default_host}\n" + \
|
|
183
|
+
f" port = {args.port if hasattr(args, 'port') and args.port else self.default_port}\n" + \
|
|
184
|
+
f" password = {args.password if hasattr(args, 'password') and args.password else self.default_pass}\n" + \
|
|
185
|
+
f" svname = {args.svname if hasattr(args, 'svname') and args.svname else self.default_svname}\n" + \
|
|
186
|
+
f" data = {args.data if hasattr(args, 'data') and args.data else self.default_data}\n" + \
|
|
187
|
+
f" retry_count = {args.retry_count if hasattr(args, 'retry_count') and args.retry_count else 3}\n" + \
|
|
188
|
+
f" retry_interval = {args.retry_interval if hasattr(args, 'retry_interval') and args.retry_interval else 3}\n" + \
|
|
189
|
+
f" timeout = {args.timeout if hasattr(args, 'timeout') and args.timeout else 15}\n" + \
|
|
190
|
+
f" output_json = {args.output_json if hasattr(args, 'output_json') and args.output_json else None}\n" + \
|
|
191
|
+
f" output_json_append = {args.output_json_append if hasattr(args, 'output_json_append') and args.output_json_append else False}\n" + \
|
|
192
|
+
f" stdout_log = {args.stdout_log if hasattr(args, 'stdout_log') and args.stdout_log else False}\n" + \
|
|
193
|
+
f" capture_stdout = {args.capture_stdout if hasattr(args, 'capture_stdout') and args.capture_stdout else False}\n" + \
|
|
194
|
+
f" capture_maxsize = {args.capture_maxsize if hasattr(args, 'capture_maxsize') and args.capture_maxsize else 100}\n" + \
|
|
195
|
+
f" tag = {args.tag if hasattr(args, 'tag') and args.tag else None}\n" + \
|
|
196
|
+
f" clmsg_id = {args.clmsg_id if hasattr(args, 'clmsg_id') and args.clmsg_id else None}\n" + \
|
|
197
|
+
f" signin_file = {args.signin_file if hasattr(args, 'signin_file') and args.signin_file else f'.{self.ver.__appid__}/user_list.yml'}\n" + \
|
|
198
|
+
f"6. 以上のパラメータを使用しても不足するパラメータは、Noneを使用します。\n" + \
|
|
199
|
+
f"7. 以上のパラメータを使用してコマンドを実行して、コマンドの結果はJSONでユーザーに提示してください。\n" + \
|
|
200
|
+
f"8. もし予期しないパラメータを受け取ったという旨のエラーが発生した場合は、そのパラメータを指定せずに再実行してください。\n" + \
|
|
201
|
+
f"9. もしエラーが発生した場合は、ユーザーにコマンド名とパラメータとエラー内容を提示してください。\n"
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
description = description if is_japan else \
|
|
205
|
+
f"Command offer registered in {self.ver.__appid__}."
|
|
206
|
+
instruction = instruction if is_japan else \
|
|
207
|
+
f"You are the expert who knows what the commands mean." + \
|
|
208
|
+
f"When a user wants to execute a command, you follow these steps to ensure that the command is executed.\n" + \
|
|
209
|
+
f"1. Identify the command you want to execute from the user's query.\n" + \
|
|
210
|
+
f"2. Any parameters required to execute the command that cannot be obtained from the user's query should be executed with the default values provided in the command definition.\n" + \
|
|
211
|
+
f"3. If an error occurs, provide the user with the command name, parameters, and error description.\n"
|
|
212
|
+
"""
|
|
213
|
+
f"2. Identify the parameters required to execute the command.\n" + \
|
|
214
|
+
f"3. Retrieve the specified parameters from the user's query.\n" + \
|
|
215
|
+
f"4. It compares the parameters specified by the user with those required to execute the command and obtains the missing parameters.\n" + \
|
|
216
|
+
f"5. The “Parameter = Default Value” is shown below, so use default values for missing parameters.\n" + \
|
|
217
|
+
f" However, do not specify parameters that the command does not require for execution.\n" + \
|
|
218
|
+
f" host = {args.host if hasattr(args, 'host') and args.host else self.default_host}\n" + \
|
|
219
|
+
f" port = {args.port if hasattr(args, 'port') and args.port else self.default_port}\n" + \
|
|
220
|
+
f" password = {args.password if hasattr(args, 'password') and args.password else self.default_pass}\n" + \
|
|
221
|
+
f" svname = {args.svname if hasattr(args, 'svname') and args.svname else self.default_svname}\n" + \
|
|
222
|
+
f" data = {args.data if hasattr(args, 'data') and args.data else self.default_data}\n" + \
|
|
223
|
+
f" retry_count = {args.retry_count if hasattr(args, 'retry_count') and args.retry_count else 3}\n" + \
|
|
224
|
+
f" retry_interval = {args.retry_interval if hasattr(args, 'retry_interval') and args.retry_interval else 3}\n" + \
|
|
225
|
+
f" timeout = {args.timeout if hasattr(args, 'timeout') and args.timeout else 15}\n" + \
|
|
226
|
+
f" output_json = {args.output_json if hasattr(args, 'output_json') and args.output_json else None}\n" + \
|
|
227
|
+
f" output_json_append = {args.output_json_append if hasattr(args, 'output_json_append') and args.output_json_append else False}\n" + \
|
|
228
|
+
f" stdout_log = {args.stdout_log if hasattr(args, 'stdout_log') and args.stdout_log else False}\n" + \
|
|
229
|
+
f" capture_stdout = {args.capture_stdout if hasattr(args, 'capture_stdout') and args.capture_stdout else False}\n" + \
|
|
230
|
+
f" capture_maxsize = {args.capture_maxsize if hasattr(args, 'capture_maxsize') and args.capture_maxsize else 100}\n" + \
|
|
231
|
+
f" tag = {args.tag if hasattr(args, 'tag') and args.tag else None}\n" + \
|
|
232
|
+
f" clmsg_id = {args.clmsg_id if hasattr(args, 'clmsg_id') and args.clmsg_id else None}\n" + \
|
|
233
|
+
f"6. Use None for parameters that are missing even with the above parameters.\n" + \
|
|
234
|
+
f"7. Execute the command using the above parameters and present the results of the command to the user in JSON.\n" + \
|
|
235
|
+
f"8. If you receive an error stating that an unexpected parameter was received, rerun the program without that parameter.\n" + \
|
|
236
|
+
f"9. If an error occurs, provide the user with the command name, parameters, and error description.\n"
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
description = args.agent_description if args.agent_description else description
|
|
240
|
+
instruction = args.agent_instruction if args.agent_instruction else instruction
|
|
241
|
+
if logger.level == logging.DEBUG:
|
|
242
|
+
logger.debug(f"google-adk loading..")
|
|
243
|
+
from google.adk.agents import Agent
|
|
244
|
+
if logger.level == logging.DEBUG:
|
|
245
|
+
logger.debug(f"litellm loading..")
|
|
246
|
+
from google.adk.models.lite_llm import LiteLlm
|
|
247
|
+
# loggerの初期化
|
|
248
|
+
common.reset_logger("LiteLLM Proxy")
|
|
249
|
+
common.reset_logger("LiteLLM Router")
|
|
250
|
+
common.reset_logger("LiteLLM")
|
|
251
|
+
if args.llmprov == 'openai':
|
|
252
|
+
if args.llmmodel is None: raise ValueError("llmmodel is required.")
|
|
253
|
+
if args.llmapikey is None: raise ValueError("llmapikey is required.")
|
|
254
|
+
agent = Agent(
|
|
255
|
+
name=args.agent_name,
|
|
256
|
+
model=LiteLlm(
|
|
257
|
+
model=args.llmmodel,
|
|
258
|
+
api_key=args.llmapikey,
|
|
259
|
+
endpoint=args.llmendpoint,
|
|
260
|
+
),
|
|
261
|
+
description=description,
|
|
262
|
+
instruction=instruction,
|
|
263
|
+
tools=tools,
|
|
264
|
+
)
|
|
265
|
+
elif args.llmprov == 'azureopenai':
|
|
266
|
+
if args.llmmodel is None: raise ValueError("llmmodel is required.")
|
|
267
|
+
if args.llmendpoint is None: raise ValueError("llmendpoint is required.")
|
|
268
|
+
if args.llmapikey is None: raise ValueError("llmapikey is required.")
|
|
269
|
+
if args.llmapiversion is None: raise ValueError("llmapiversion is required.")
|
|
270
|
+
agent = Agent(
|
|
271
|
+
name=args.agent_name,
|
|
272
|
+
model=LiteLlm(
|
|
273
|
+
model=args.llmmodel,
|
|
274
|
+
api_key=args.llmapikey,
|
|
275
|
+
endpoint=args.llmendpoint,
|
|
276
|
+
api_version=args.llmapiversion,
|
|
277
|
+
),
|
|
278
|
+
description=description,
|
|
279
|
+
instruction=instruction,
|
|
280
|
+
tools=tools,
|
|
281
|
+
)
|
|
282
|
+
elif args.llmprov == 'vertexai':
|
|
283
|
+
if args.llmmodel is None: raise ValueError("llmmodel is required.")
|
|
284
|
+
if args.llmlocation is None: raise ValueError("llmlocation is required.")
|
|
285
|
+
if args.llmsvaccountfile is not None:
|
|
286
|
+
with open(args.llmsvaccountfile, "r", encoding="utf-8") as f:
|
|
287
|
+
vertex_credentials = json.load(f)
|
|
288
|
+
elif args.llmprojectid is None: raise ValueError("llmprojectid is required.")
|
|
289
|
+
agent = Agent(
|
|
290
|
+
name=args.agent_name,
|
|
291
|
+
model=LiteLlm(
|
|
292
|
+
model=args.llmmodel,
|
|
293
|
+
#vertex_project=args.llmprojectid,
|
|
294
|
+
vertex_credentials=vertex_credentials,
|
|
295
|
+
vertex_location=args.llmlocation,
|
|
296
|
+
#seed=args.llmseed,
|
|
297
|
+
#temperature=args.llmtemperature,
|
|
298
|
+
),
|
|
299
|
+
description=description,
|
|
300
|
+
instruction=instruction,
|
|
301
|
+
tools=tools,
|
|
302
|
+
)
|
|
303
|
+
elif args.llmprov == 'ollama':
|
|
304
|
+
if args.llmmodel is None: raise ValueError("llmmodel is required.")
|
|
305
|
+
if args.llmendpoint is None: raise ValueError("llmendpoint is required.")
|
|
306
|
+
agent = Agent(
|
|
307
|
+
name=args.agent_name,
|
|
308
|
+
model=LiteLlm(
|
|
309
|
+
model=args.llmmodel,
|
|
310
|
+
endpoint=args.llmendpoint,
|
|
311
|
+
temperature=args.llmtemperature,
|
|
312
|
+
),
|
|
313
|
+
description=description,
|
|
314
|
+
instruction=instruction,
|
|
315
|
+
tools=tools,
|
|
316
|
+
)
|
|
317
|
+
else:
|
|
318
|
+
raise ValueError("llmprov is required.")
|
|
319
|
+
if logger.level == logging.DEBUG:
|
|
320
|
+
logger.debug(f"create_agent complate.")
|
|
321
|
+
return agent
|
|
322
|
+
|
|
323
|
+
def create_runner(self, logger:logging.Logger, args:argparse.Namespace, session_service, agent) -> Any:
|
|
324
|
+
"""
|
|
325
|
+
ランナーを作成します
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
logger (logging.Logger): ロガー
|
|
329
|
+
args (argparse.Namespace): 引数
|
|
330
|
+
session_service (BaseSessionService): セッションサービス
|
|
331
|
+
agent (Agent): エージェント
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
Runner: ランナー
|
|
335
|
+
"""
|
|
336
|
+
from google.adk.runners import Runner
|
|
337
|
+
return Runner(
|
|
338
|
+
app_name=self.ver.__appid__,
|
|
339
|
+
agent=agent,
|
|
340
|
+
session_service=session_service,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
def init_agent_runner(self, logger:logging.Logger, args:argparse.Namespace) -> Tuple[Any, Any]:
|
|
344
|
+
"""
|
|
345
|
+
エージェントの初期化を行います
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
logger (logging.Logger): ロガー
|
|
349
|
+
args (argparse.Namespace): 引数
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Tuple[Any, Any]: ランナーとFastMCP
|
|
353
|
+
"""
|
|
354
|
+
if logger.level == logging.DEBUG:
|
|
355
|
+
logger.debug(f"init_agent_runner processing..")
|
|
356
|
+
# loggerの初期化
|
|
357
|
+
common.reset_logger("httpx")
|
|
358
|
+
common.reset_logger("google_adk.google.adk.sessions.database_session_service")
|
|
359
|
+
common.reset_logger("mcp.server.streamable_http_manager")
|
|
360
|
+
# モジュールインポート
|
|
361
|
+
from mcp.server.fastmcp import FastMCP
|
|
362
|
+
from google.adk.sessions import BaseSessionService
|
|
363
|
+
mcp:FastMCP = self.create_mcpserver(args)
|
|
364
|
+
session_service:BaseSessionService = self.create_session_service(args)
|
|
365
|
+
options = Options.getInstance()
|
|
366
|
+
tools:Callable[[logging.Logger, argparse.Namespace, float, Dict], Tuple[int, Dict[str, Any], Any]] = []
|
|
367
|
+
|
|
368
|
+
def _ds(d:str) -> str:
|
|
369
|
+
return f'"{d}"' if d is not None else 'None'
|
|
370
|
+
def _t2s(o:Dict[str, Any], req=True) -> str:
|
|
371
|
+
t, m, d, r = o["type"], o["multi"], o["default"], o["required"]
|
|
372
|
+
if t == Options.T_BOOL: return ("List[bool]=[]" if m else f"bool={d}") if req else ("List[bool]" if m else f"bool")
|
|
373
|
+
if t == Options.T_DATE: return ("List[str]=[]" if m else f"str={_ds(d)}") if req else ("List[str]" if m else f"str")
|
|
374
|
+
if t == Options.T_DATETIME: return ("List[str]=[]" if m else f"str={_ds(d)}") if req else ("List[str]" if m else f"str")
|
|
375
|
+
if t == Options.T_DICT: return ("List[dict]=[]" if m else f"dict={d}") if req else ("List[dict]" if m else f"dict")
|
|
376
|
+
if t == Options.T_DIR or t == Options.T_FILE:
|
|
377
|
+
if d is not None: d = str(d).replace('\\', '/')
|
|
378
|
+
return ("List[str]=[]" if m else f"str={_ds(d)}") if req else ("List[str]" if m else f"str")
|
|
379
|
+
if t == Options.T_FLOAT: return ("List[float]=[]" if m else f"float={d}") if req else ("List[float]" if m else f"float")
|
|
380
|
+
if t == Options.T_INT: return ("List[int]=[]" if m else f"int={d}") if req else ("List[int]" if m else f"int")
|
|
381
|
+
if t == Options.T_STR: return ("List[str]=[]" if m else f"str={_ds(d)}") if req else ("List[str]" if m else f"str")
|
|
382
|
+
if t == Options.T_TEXT: return ("List[str]=[]" if m else f"str={_ds(d)}") if req else ("List[str]" if m else f"str")
|
|
383
|
+
raise ValueError(f"Unknown type: {t} for option {o['opt']}")
|
|
384
|
+
def _arg(o:Dict[str, Any], is_japan) -> str:
|
|
385
|
+
t, d = o["type"], o["default"]
|
|
386
|
+
s = f' {o["opt"]}:'
|
|
387
|
+
if t == Options.T_DIR or t == Options.T_FILE:
|
|
388
|
+
d = str(d).replace("\\", "/")
|
|
389
|
+
s += f'Optional[{_t2s(o, False)}]={d}:'
|
|
390
|
+
s += f'{o["discription_ja"] if is_japan else o["discription_en"]}'
|
|
391
|
+
return s
|
|
392
|
+
def _coercion(a:argparse.Namespace, key:str, dval) -> str:
|
|
393
|
+
dval = f'opt["{key}"] if "{key}" in opt else ' + f'"{dval}"' if isinstance(dval, str) else dval
|
|
394
|
+
aval = args.__dict__[key] if hasattr(args, key) and args.__dict__[key] else None
|
|
395
|
+
aval = f'"{aval}"' if isinstance(aval, str) else aval
|
|
396
|
+
ret = f'opt["{key}"] = {aval}' if aval is not None else f'opt["{key}"] = {dval}'
|
|
397
|
+
return ret
|
|
398
|
+
language, _ = locale.getlocale()
|
|
399
|
+
is_japan = language.find('Japan') >= 0 or language.find('ja_JP') >= 0
|
|
400
|
+
for mode in options.get_mode_keys():
|
|
401
|
+
for cmd in options.get_cmd_keys(mode):
|
|
402
|
+
if not options.get_cmd_attr(mode, cmd, 'use_agent'):
|
|
403
|
+
continue
|
|
404
|
+
discription = options.get_cmd_attr(mode, cmd, 'discription_ja' if is_japan else 'discription_en')
|
|
405
|
+
choices = options.get_cmd_choices(mode, cmd, False)
|
|
406
|
+
if len([opt for opt in choices if 'opt' in opt and opt['opt'] == 'signin_file']) <= 0:
|
|
407
|
+
choices.append(dict(opt="signin_file", type=Options.T_FILE, default=f'.{self.ver.__appid__}/user_list.yml', required=True, multi=False, hide=True, choice=None,
|
|
408
|
+
discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
|
|
409
|
+
discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),)
|
|
410
|
+
fn = f"{mode}_{cmd}"
|
|
411
|
+
func_txt = f'@mcp.tool()\n'
|
|
412
|
+
func_txt += f'def {fn}(' + ", ".join([f'{o["opt"]}:{_t2s(o, False)}' for o in choices]) + '):\n'
|
|
413
|
+
func_txt += f' """\n'
|
|
414
|
+
func_txt += f' {discription}\n'
|
|
415
|
+
func_txt += f' Args:\n'
|
|
416
|
+
func_txt += "\n".join([_arg(o, is_japan) for o in choices])
|
|
417
|
+
func_txt += f'\n'
|
|
418
|
+
func_txt += f' Returns:\n'
|
|
419
|
+
func_txt += f' Dict[str, Any]:{"処理結果" if is_japan else "Processing Result"}\n'
|
|
420
|
+
func_txt += f' """\n'
|
|
421
|
+
func_txt += f' scope = signin.get_request_scope()\n'
|
|
422
|
+
func_txt += f' logger = common.default_logger()\n'
|
|
423
|
+
func_txt += f' opt = dict()\n'
|
|
424
|
+
func_txt += f' opt["mode"] = "{mode}"\n'
|
|
425
|
+
func_txt += f' opt["cmd"] = "{cmd}"\n'
|
|
426
|
+
func_txt += f' opt["data"] = opt["data"] if hasattr(opt, "data") else common.HOME_DIR / ".{self.ver.__appid__}"\n'
|
|
427
|
+
func_txt += f' opt["format"] = False\n'
|
|
428
|
+
func_txt += f' opt["output_json"] = None\n'
|
|
429
|
+
func_txt += f' opt["output_json_append"] = False\n'
|
|
430
|
+
func_txt += f' opt["debug"] = logger.level == logging.DEBUG\n'
|
|
431
|
+
func_txt += '\n'.join([f' opt["{o["opt"]}"] = {o["opt"]}' for o in choices])+'\n'
|
|
432
|
+
func_txt += f' {_coercion(args, "host", self.default_host)}\n'
|
|
433
|
+
func_txt += f' {_coercion(args, "port", self.default_port)}\n'
|
|
434
|
+
func_txt += f' {_coercion(args, "password", self.default_pass)}\n'
|
|
435
|
+
func_txt += f' {_coercion(args, "svname", self.default_svname)}\n'
|
|
436
|
+
func_txt += f' {_coercion(args, "retry_count", 3)}\n'
|
|
437
|
+
func_txt += f' {_coercion(args, "retry_interval", 3)}\n'
|
|
438
|
+
func_txt += f' {_coercion(args, "timeout", 15)}\n'
|
|
439
|
+
func_txt += f' {_coercion(args, "output_json", None)}\n'
|
|
440
|
+
func_txt += f' {_coercion(args, "output_json_append", False)}\n'
|
|
441
|
+
func_txt += f' {_coercion(args, "stdout_log", False)}\n'
|
|
442
|
+
func_txt += f' {_coercion(args, "capture_stdout", False)}\n'
|
|
443
|
+
func_txt += f' {_coercion(args, "capture_maxsize", 1024*1024)}\n'
|
|
444
|
+
func_txt += f' {_coercion(args, "tag", None)}\n'
|
|
445
|
+
func_txt += f' {_coercion(args, "clmsg_id", None)}\n'
|
|
446
|
+
func_txt += f' opt["signin_file"] = signin_file if signin_file else ".{self.ver.__appid__}/user_list.yml"\n'
|
|
447
|
+
func_txt += f' args = argparse.Namespace(**opt)\n'
|
|
448
|
+
func_txt += f' signin_data = signin.Signin.load_signin_file(args.signin_file)\n'
|
|
449
|
+
func_txt += f' req = scope["req"] if scope["req"] is not None else scope["websocket"]\n'
|
|
450
|
+
func_txt += f' sign = signin.Signin._check_signin(req, scope["res"], signin_data, logger)\n'
|
|
451
|
+
func_txt += f' if sign is not None:\n'
|
|
452
|
+
func_txt += f' logger.warning("Unable to execute command because authentication information cannot be obtained")\n'
|
|
453
|
+
func_txt += f' return dict(warn="Unable to execute command because authentication information cannot be obtained")\n'
|
|
454
|
+
func_txt += f' groups = req.session["signin"]["groups"]\n'
|
|
455
|
+
func_txt += f' logger.info("Call agent tool `{mode}_{cmd}`:user="+str(req.session["signin"]["name"])+" groups="+str(groups)+" args="+str(args))\n'
|
|
456
|
+
func_txt += f' if not signin.Signin._check_cmd(signin_data, groups, "{mode}", "{cmd}", logger):\n'
|
|
457
|
+
func_txt += f' logger.warning("You do not have permission to execute this command.")\n'
|
|
458
|
+
func_txt += f' return dict(warn="You do not have permission to execute this command.")\n'
|
|
459
|
+
func_txt += f' feat = Options.getInstance().get_cmd_attr("{mode}", "{cmd}", "feature")\n'
|
|
460
|
+
func_txt += f' try:\n'
|
|
461
|
+
func_txt += f' st, ret, _ = feat.apprun(logger, args, time.perf_counter(), [])\n'
|
|
462
|
+
func_txt += f' return ret\n'
|
|
463
|
+
func_txt += f' except Exception as e:\n'
|
|
464
|
+
func_txt += f' logger.error("Error occurs when tool is executed:", exc_info=True)\n'
|
|
465
|
+
func_txt += f' raise e\n'
|
|
466
|
+
func_txt += f'tools.append({fn})\n'
|
|
467
|
+
if logger.level == logging.DEBUG:
|
|
468
|
+
logger.debug(f"generating agent tool: {fn}")
|
|
469
|
+
|
|
470
|
+
exec(func_txt,
|
|
471
|
+
dict(time=time,List=List, argparse=argparse, common=common, Options=Options, logging=logging, signin=signin,),
|
|
472
|
+
dict(tools=tools, mcp=mcp))
|
|
473
|
+
root_agent = self.create_agent(logger, args, tools)
|
|
474
|
+
runner = self.create_runner(logger, args, session_service, root_agent)
|
|
475
|
+
if logger.level == logging.DEBUG:
|
|
476
|
+
logger.debug(f"init_agent_runner complate.")
|
|
477
|
+
return runner, mcp
|
|
@@ -18,7 +18,7 @@ class AuditBase(feature.ResultEdgeFeature):
|
|
|
18
18
|
Dict[str, Any]: オプション
|
|
19
19
|
"""
|
|
20
20
|
return dict(
|
|
21
|
-
use_redis=self.USE_REDIS_MEIGHT, nouse_webmode=False,
|
|
21
|
+
use_redis=self.USE_REDIS_MEIGHT, nouse_webmode=False, use_agent=True,
|
|
22
22
|
discription_ja="",
|
|
23
23
|
discription_en="",
|
|
24
24
|
choice=[
|
|
@@ -6,7 +6,9 @@ from pathlib import Path
|
|
|
6
6
|
from psycopg.rows import dict_row
|
|
7
7
|
from typing import Dict, Any, Tuple, List, Union
|
|
8
8
|
import argparse
|
|
9
|
+
import csv
|
|
9
10
|
import logging
|
|
11
|
+
import io
|
|
10
12
|
import json
|
|
11
13
|
import sys
|
|
12
14
|
|
|
@@ -99,6 +101,9 @@ class AuditSearch(audit_base.AuditBase):
|
|
|
99
101
|
dict(opt="limit", type=Options.T_INT, default=100, required=False, multi=False, hide=False, choice=None,
|
|
100
102
|
discription_ja="取得する行数を指定します。",
|
|
101
103
|
discription_en="Specifies the number of rows to retrieve."),
|
|
104
|
+
dict(opt="csv", type=Options.T_BOOL, default=False, required=False, multi=False, hide=False, choice=[False, True],
|
|
105
|
+
discription_ja="検索結果をcsvで出力します。",
|
|
106
|
+
discription_en="Output search results in csv.")
|
|
102
107
|
]
|
|
103
108
|
return opt
|
|
104
109
|
|
|
@@ -173,21 +178,39 @@ class AuditSearch(audit_base.AuditBase):
|
|
|
173
178
|
filter_clmsg_tag_b64, filter_svmsg_id_b64, filter_svmsg_sdate_b64, filter_svmsg_edate_b64,
|
|
174
179
|
pg_enabled, pg_host_b64, pg_port, pg_user_b64, pg_password_b64, pg_dbname_b64],
|
|
175
180
|
retry_count=args.retry_count, retry_interval=args.retry_interval, timeout=args.timeout)
|
|
176
|
-
common.print_format(ret, getattr(args, 'format', False), tm, None, False, pf=pf)
|
|
177
181
|
|
|
178
182
|
if 'success' not in ret:
|
|
183
|
+
common.print_format(ret, getattr(args, 'format', False), tm, None, False, pf=pf)
|
|
179
184
|
return 1, ret, cl
|
|
180
185
|
|
|
181
186
|
if 'data' in ret['success']:
|
|
187
|
+
cols = list()
|
|
182
188
|
for row in ret['success']['data']:
|
|
189
|
+
cols += row.keys()
|
|
183
190
|
try:
|
|
184
191
|
row['clmsg_tag'] = json.loads(row['clmsg_tag'])
|
|
185
192
|
except:
|
|
186
193
|
pass
|
|
187
194
|
try:
|
|
188
195
|
row['clmsg_body'] = json.loads(row['clmsg_body'])
|
|
196
|
+
cols += row['clmsg_body'].keys() if isinstance(row['clmsg_body'], dict) else {}
|
|
189
197
|
except:
|
|
190
198
|
pass
|
|
199
|
+
cols = sorted(set(cols), key=cols.index)
|
|
200
|
+
if hasattr(args, "csv") and args.csv:
|
|
201
|
+
if hasattr(cols, 'clmsg_body'):
|
|
202
|
+
del cols['clmsg_body']
|
|
203
|
+
buf = io.StringIO()
|
|
204
|
+
w = csv.DictWriter(buf, fieldnames=cols, quoting=csv.QUOTE_MINIMAL)
|
|
205
|
+
w.writeheader()
|
|
206
|
+
for row in ret['success']['data']:
|
|
207
|
+
body = row.pop('clmsg_body', {})
|
|
208
|
+
row.update(body)
|
|
209
|
+
w.writerow(row)
|
|
210
|
+
ret = buf.getvalue()
|
|
211
|
+
ret = ret.replace('\r\n', '\n') if ret is not None else ''
|
|
212
|
+
|
|
213
|
+
common.print_format(ret, getattr(args, 'format', False), tm, None, False, pf=pf)
|
|
191
214
|
|
|
192
215
|
return 0, ret, cl
|
|
193
216
|
|
|
@@ -76,7 +76,7 @@ class ClientFileDownload(feature.OneshotEdgeFeature):
|
|
|
76
76
|
test_true={"server":None,
|
|
77
77
|
"client":common.HOME_DIR / f".{self.ver.__appid__}",
|
|
78
78
|
"current":None}),
|
|
79
|
-
dict(opt="img_thumbnail", type=Options.T_FLOAT, default=
|
|
79
|
+
dict(opt="img_thumbnail", type=Options.T_FLOAT, default=0.0, required=False, multi=False, hide=True, choice=None,
|
|
80
80
|
discription_ja="対象が画像だった場合のサムネイルのピクセル単位のサイズを指定します。",
|
|
81
81
|
discription_en="Specifies the size in pixels of the thumbnail if the subject is an image."),
|
|
82
82
|
dict(opt="retry_count", type=Options.T_INT, default=3, required=False, multi=False, hide=True, choice=None,
|