cmdbox 0.6.0.3__py3-none-any.whl → 0.6.1__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/app.py +2 -2
- cmdbox/app/auth/signin.py +144 -28
- cmdbox/app/common.py +44 -5
- cmdbox/app/edge.py +1 -1
- cmdbox/app/edge_tool.py +1 -1
- cmdbox/app/features/cli/agent_base.py +2 -394
- cmdbox/app/features/cli/cmdbox_audit_search.py +0 -2
- cmdbox/app/features/cli/cmdbox_audit_write.py +9 -0
- cmdbox/app/features/cli/cmdbox_cmd_list.py +2 -2
- cmdbox/app/features/cli/cmdbox_cmd_load.py +2 -2
- cmdbox/app/features/cli/cmdbox_mcp_proxy.py +90 -0
- 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_gencert.py +25 -2
- 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 +68 -26
- 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 +10 -4
- cmdbox/app/features/web/cmdbox_web_signin.py +1 -1
- cmdbox/app/mcp.py +375 -0
- cmdbox/app/options.py +10 -0
- cmdbox/app/web.py +60 -53
- cmdbox/autoload.py +10 -0
- cmdbox/extensions/user_list.yml +25 -1
- cmdbox/licenses/{LICENSE.Deprecated.1.2.18(MIT License).txt → LICENSE_PyJWT_2_10_1_MIT_License.txt} +2 -2
- cmdbox/licenses/LICENSE_exceptiongroup_1_3_0_MIT_License.txt +73 -0
- cmdbox/licenses/LICENSE_fastmcp_2_9_2_Apache_Software_License.txt +201 -0
- cmdbox/licenses/{LICENSE.graphviz.0.20.3(MIT License).txt → LICENSE_graphviz_0_21_UNKNOWN.txt} +1 -1
- cmdbox/licenses/LICENSE_jaraco_functools_4_2_1_UNKNOWN.txt +18 -0
- cmdbox/licenses/{LICENSE.numpy.2.2.5(BSD License).txt → LICENSE_numpy_2_3_1_BSD_License.txt} +8 -8
- cmdbox/licenses/LICENSE_openapi-pydantic_0_5_1_MIT_License.txt +40 -0
- cmdbox/licenses/LICENSE_propcache_0_3_2_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_proto-plus_1_26_1_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_shellingham_1_5_4_ISC_License-ISCL.txt +13 -0
- cmdbox/licenses/LICENSE_sphinx-last-updated-by-git_0_3_8_BSD_License.txt +22 -0
- cmdbox/licenses/LICENSE_tenacity_8_5_0_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_tokenizers_0_21_2_Apache_Software_License.txt +1 -0
- cmdbox/licenses/LICENSE_typer_0_16_0_MIT_License.txt +21 -0
- cmdbox/licenses/LICENSE_typing-inspection_0_4_1_UNKNOWN.txt +21 -0
- cmdbox/licenses/LICENSE_yarl_1_20_1_Apache_Software_License.txt +202 -0
- cmdbox/licenses/LICENSE_zipp_3_23_0_UNKNOWN.txt +18 -0
- cmdbox/licenses/files.txt +176 -166
- cmdbox/logconf_mcp.yml +43 -0
- cmdbox/version.py +2 -2
- cmdbox/web/agent.html +1 -33
- cmdbox/web/assets/cmdbox/agent.js +2 -3
- cmdbox/web/assets/cmdbox/audit.js +1 -1
- cmdbox/web/assets/cmdbox/common.js +73 -8
- cmdbox/web/assets/cmdbox/main.js +1 -2
- cmdbox/web/assets/cmdbox/svgicon.js +122 -0
- cmdbox/web/assets/cmdbox/users.js +4 -3
- cmdbox/web/audit.html +1 -35
- cmdbox/web/filer.html +1 -13
- cmdbox/web/gui.html +2 -50
- cmdbox/web/result.html +2 -46
- cmdbox/web/signin.html +1 -26
- cmdbox/web/users.html +1 -41
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/METADATA +32 -7
- cmdbox-0.6.1.dist-info/RECORD +383 -0
- cmdbox/licenses/LICENSE.keyring.25.6.0(MIT License).txt +0 -17
- cmdbox/licenses/LICENSE.typing-inspection.0.4.0(MIT License).txt +0 -21
- cmdbox/licenses/LICENSE.wrapt.1.17.2(BSD License).txt +0 -24
- cmdbox/licenses/LICENSE.zipp.3.21.0(MIT License).txt +0 -17
- cmdbox-0.6.0.3.dist-info/RECORD +0 -368
- /cmdbox/licenses/{LICENSE.Authlib.1.5.2(BSD License).txt → LICENSE_Authlib_1_6_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.Jinja2.3.1.6(BSD License).txt → LICENSE_Jinja2_3_1_6_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.MarkupSafe.3.0.2(BSD License).txt → LICENSE_MarkupSafe_3_0_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.PyYAML.6.0.2(MIT License).txt → LICENSE_PyYAML_6_0_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.Pygments.2.19.1(BSD License).txt → LICENSE_Pygments_2_19_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.SQLAlchemy.2.0.40(MIT License).txt → LICENSE_SQLAlchemy_2_0_41_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.Sphinx.8.2.3(UNKNOWN).txt → LICENSE_Sphinx_8_2_3_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.aiohappyeyeballs.2.6.1(Python Software Foundation License).txt → LICENSE_aiohappyeyeballs_2_6_1_Python_Software_Foundation_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.aiohttp.3.11.18(Apache Software License).txt → LICENSE_aiohttp_3_12_13_Apache-2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.aiosignal.1.3.2(Apache Software License).txt → LICENSE_aiosignal_1_3_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.alabaster.1.0.0(BSD License).txt → LICENSE_alabaster_1_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.annotated-types.0.7.0(MIT License).txt → LICENSE_annotated-types_0_7_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.anyio.4.9.0(MIT License).txt → LICENSE_anyio_4_9_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.argcomplete.3.6.2(Apache Software License).txt → LICENSE_argcomplete_3_6_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.async-timeout.5.0.1(Apache Software License).txt → LICENSE_async-timeout_5_0_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.attrs.25.3.0(UNKNOWN).txt → LICENSE_attrs_25_3_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.babel.2.17.0(BSD License).txt → LICENSE_babel_2_17_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.backports.tarfile.1.2.0(MIT License).txt → LICENSE_backports_tarfile_1_2_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.cachetools.5.5.2(MIT License).txt → LICENSE_cachetools_5_5_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.certifi.2025.4.26(Mozilla Public License 2.0 (MPL 2.0)).txt → LICENSE_certifi_2025_6_15_Mozilla_Public_License_2_0-MPL_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.cffi.1.17.1(MIT License).txt → LICENSE_cffi_1_17_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.charset-normalizer.3.4.2(MIT License).txt → LICENSE_charset-normalizer_3_4_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.click.8.2.0(UNKNOWN).txt → LICENSE_click_8_2_1_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jiter.0.9.0(MIT License).txt → LICENSE_cloudpickle_3_1_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.colorama.0.4.6(BSD License).txt → LICENSE_colorama_0_4_6_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.cryptography.44.0.3(Apache Software License; BSD License).txt → LICENSE_cryptography_45_0_4_Apache-2_0_OR_BSD-3-Clause.txt} +0 -0
- /cmdbox/licenses/{LICENSE.distro.1.9.0(Apache Software License).txt → LICENSE_distro_1_9_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.docstring_parser.0.16(MIT License).txt → LICENSE_docstring_parser_0_16_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.docutils.0.21.2(BSD License; GNU General Public License (GPL); Public Domain; Python Software Foundation License).txt → LICENSE_docutils_0_21_2_BSD_License-GNU_General_Public_License-GPL-Public_Domain-Python_Software_Foundation_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.fastapi.0.115.12(MIT License).txt → LICENSE_fastapi_0_115_14_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.filelock.3.18.0(The Unlicense (Unlicense)).txt → LICENSE_filelock_3_18_0_The_Unlicense-Unlicense.txt} +0 -0
- /cmdbox/licenses/{LICENSE.frozenlist.1.6.0(Apache-2.0).txt → LICENSE_frozenlist_1_7_0_Apache-2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.fsspec.2025.3.2(BSD License).txt → LICENSE_fsspec_2025_5_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.gevent.25.4.2(MIT).txt → LICENSE_gevent_25_5_1_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-adk.0.5.0(Apache Software License).txt → LICENSE_google-adk_1_5_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-api-core.2.24.2(Apache Software License).txt → LICENSE_google-api-core_2_25_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-api-python-client.2.169.0(Apache Software License).txt → LICENSE_google-api-python-client_2_174_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-auth-httplib2.0.2.0(Apache Software License).txt → LICENSE_google-auth-httplib2_0_2_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-auth.2.40.1(Apache Software License).txt → LICENSE_google-auth_2_40_3_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-aiplatform.1.92.0(Apache 2.0).txt → LICENSE_google-cloud-aiplatform_1_100_0_Apache_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-bigquery.3.31.0(Apache Software License).txt → LICENSE_google-cloud-appengine-logging_1_6_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-core.2.4.3(Apache Software License).txt → LICENSE_google-cloud-audit-log_0_3_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-resource-manager.1.14.2(Apache Software License).txt → LICENSE_google-cloud-bigquery_3_34_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-secret-manager.2.23.3(Apache Software License).txt → LICENSE_google-cloud-core_2_4_3_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-speech.2.32.0(Apache Software License).txt → LICENSE_google-cloud-logging_3_12_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-storage.2.19.0(Apache Software License).txt → LICENSE_google-cloud-resource-manager_1_14_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-cloud-trace.1.16.1(Apache Software License).txt → LICENSE_google-cloud-secret-manager_2_24_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-crc32c.1.7.1(Apache 2.0).txt → LICENSE_google-cloud-speech_2_33_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-genai.1.14.0(Apache Software License).txt → LICENSE_google-cloud-storage_2_19_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.google-resumable-media.2.7.2(Apache Software License).txt → LICENSE_google-cloud-trace_1_16_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.googleapis-common-protos.1.70.0(Apache Software License).txt → LICENSE_google-crc32c_1_7_1_Apache_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.grpc-google-iam-v1.0.14.2(Apache Software License).txt → LICENSE_google-genai_1_23_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.id.1.5.0(Apache Software License).txt → LICENSE_google-resumable-media_2_7_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.importlib_metadata.8.6.1(Apache Software License).txt → LICENSE_googleapis-common-protos_1_70_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.greenlet.3.2.2(MIT AND Python-2.0).txt → LICENSE_greenlet_3_2_3_MIT_AND_Python-2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.propcache.0.3.1(Apache Software License).txt → LICENSE_grpc-google-iam-v1_0_14_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.grpcio-status.1.71.0(Apache Software License).txt → LICENSE_grpcio-status_1_73_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.grpcio.1.71.0(Apache Software License).txt → LICENSE_grpcio_1_73_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.gunicorn.23.0.0(MIT License).txt → LICENSE_gunicorn_23_0_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.h11.0.16.0(MIT License).txt → LICENSE_h11_0_16_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httpcore.1.0.9(BSD License).txt → LICENSE_httpcore_1_0_9_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httplib2.0.22.0(MIT License).txt → LICENSE_httplib2_0_22_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httptools.0.6.4(MIT License).txt → LICENSE_httptools_0_6_4_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httpx-sse.0.4.0(MIT).txt → LICENSE_httpx-sse_0_4_1_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.httpx.0.28.1(BSD License).txt → LICENSE_httpx_0_28_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.huggingface-hub.0.31.1(Apache Software License).txt → LICENSE_huggingface-hub_0_33_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.proto-plus.1.26.1(Apache Software License).txt → LICENSE_id_1_5_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.idna.3.10(BSD License).txt → LICENSE_idna_3_10_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.imagesize.1.4.1(MIT License).txt → LICENSE_imagesize_1_4_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.yarl.1.20.0(Apache Software License).txt → LICENSE_importlib_metadata_8_7_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.itsdangerous.2.2.0(BSD License).txt → LICENSE_itsdangerous_2_2_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jaraco.classes.3.4.0(MIT License).txt → LICENSE_jaraco_classes_3_4_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jaraco.context.6.0.1(MIT License).txt → LICENSE_jaraco_context_6_0_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-applehelp.2.0.0(BSD License).txt → LICENSE_jiter_0_10_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jsonschema-specifications.2025.4.1(UNKNOWN).txt → LICENSE_jsonschema-specifications_2025_4_1_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jsonschema.4.23.0(MIT License).txt → LICENSE_jsonschema_4_24_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.jaraco.functools.4.1.0(MIT License).txt → LICENSE_keyring_25_6_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.litellm.1.69.0(MIT License).txt → LICENSE_litellm_1_73_6_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.markdown-it-py.3.0.0(MIT License).txt → LICENSE_markdown-it-py_3_0_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.mcp.1.8.0(MIT License).txt → LICENSE_mcp_1_9_4_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.mdurl.0.1.2(MIT License).txt → LICENSE_mdurl_0_1_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.more-itertools.10.7.0(MIT License).txt → LICENSE_more-itertools_10_7_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.multidict.6.4.3(Apache Software License).txt → LICENSE_multidict_6_6_2_Apache_License_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.nh3.0.2.21(MIT).txt → LICENSE_nh3_0_2_21_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.openai.1.75.0(Apache Software License).txt → LICENSE_openai_1_93_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-api.1.33.0(Apache Software License).txt → LICENSE_opentelemetry-api_1_34_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-exporter-gcp-trace.1.9.0(Apache Software License).txt → LICENSE_opentelemetry-exporter-gcp-trace_1_9_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-resourcedetector-gcp.1.9.0a0(Apache Software License).txt → LICENSE_opentelemetry-resourcedetector-gcp_1_9_0a0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-sdk.1.33.0(Apache Software License).txt → LICENSE_opentelemetry-sdk_1_34_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.opentelemetry-semantic-conventions.0.54b0(Apache Software License).txt → LICENSE_opentelemetry-semantic-conventions_0_55b1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.packaging.25.0(Apache Software License; BSD License).txt → LICENSE_packaging_25_0_Apache_Software_License-BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pillow.11.2.1(UNKNOWN).txt → LICENSE_pillow_11_2_1_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pip.24.0(MIT License).txt → LICENSE_pip_24_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.plyer.2.1.0(MIT License).txt → LICENSE_plyer_2_1_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.prettytable.3.16.0(UNKNOWN).txt → LICENSE_prettytable_3_16_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.prompt_toolkit.3.0.51(BSD License).txt → LICENSE_prompt_toolkit_3_0_51_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.protobuf.5.29.4(3-Clause BSD License).txt → LICENSE_protobuf_6_31_1_3-Clause_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.psycopg-binary.3.2.7(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE_psycopg-binary_3_2_9_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
- /cmdbox/licenses/{LICENSE.psycopg.3.2.7(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE_psycopg_3_2_9_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pyasn1.0.6.1(BSD License).txt → LICENSE_pyasn1_0_6_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pyasn1_modules.0.4.2(BSD License).txt → LICENSE_pyasn1_modules_0_4_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pycparser.2.22(BSD License).txt → LICENSE_pycparser_2_22_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pycryptodome.3.22.0(BSD License; Public Domain).txt → LICENSE_pycryptodome_3_23_0_BSD_License-Public_Domain.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic-settings.2.9.1(MIT License).txt → LICENSE_pydantic-settings_2_10_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic.2.11.4(MIT License).txt → LICENSE_pydantic_2_11_7_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pydantic_core.2.33.2(MIT License).txt → LICENSE_pydantic_core_2_33_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pyparsing.3.2.3(MIT License).txt → LICENSE_pyparsing_3_2_3_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pystray.0.19.5(GNU Lesser General Public License v3 (LGPLv3)).txt → LICENSE_pystray_0_19_5_GNU_Lesser_General_Public_License_v3-LGPLv3.txt} +0 -0
- /cmdbox/licenses/{LICENSE.python-dateutil.2.9.0.post0(Apache Software License; BSD License).txt → LICENSE_python-dateutil_2_9_0_post0_Apache_Software_License-BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.python-dotenv.1.1.0(BSD License).txt → LICENSE_python-dotenv_1_1_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.python-multipart.0.0.20(Apache Software License).txt → LICENSE_python-multipart_0_0_20_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.pywin32-ctypes.0.2.3(BSD-3-Clause).txt → LICENSE_pywin32-ctypes_0_2_3_BSD-3-Clause.txt} +0 -0
- /cmdbox/licenses/{LICENSE.questionary.2.1.0(MIT License).txt → LICENSE_questionary_2_1_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.readme_renderer.44.0(Apache Software License).txt → LICENSE_readme_renderer_44_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.redis.6.0.0(MIT License).txt → LICENSE_redis_6_2_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.referencing.0.36.2(UNKNOWN).txt → LICENSE_referencing_0_36_2_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.regex.2024.11.6(Apache Software License).txt → LICENSE_regex_2024_11_6_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.requests-toolbelt.1.0.0(Apache Software License).txt → LICENSE_requests-toolbelt_1_0_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.requests.2.32.3(Apache Software License).txt → LICENSE_requests_2_32_4_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.rfc3986.2.0.0(Apache Software License).txt → LICENSE_rfc3986_2_0_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.rich.14.0.0(MIT License).txt → LICENSE_rich_14_0_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.roman-numerals-py.3.1.0(CC0 1.0 Universal (CC0 1.0) Public Domain Dedication; Zero-Clause BSD (0BSD)).txt → LICENSE_roman-numerals-py_3_1_0_CC0_1_0_Universal-CC0_1_0-Public_Domain_Dedication-Zero-Clause_BSD-0BSD.txt} +0 -0
- /cmdbox/licenses/{LICENSE.rpds-py.0.24.0(MIT).txt → LICENSE_rpds-py_0_25_1_MIT.txt} +0 -0
- /cmdbox/licenses/{LICENSE.rsa.4.9.1(Apache Software License).txt → LICENSE_rsa_4_9_1_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.setuptools.65.5.0(MIT License).txt → LICENSE_setuptools_65_5_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.shapely.2.1.0(BSD License).txt → LICENSE_shapely_2_1_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.six.1.17.0(MIT License).txt → LICENSE_six_1_17_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sniffio.1.3.1(Apache Software License; MIT License).txt → LICENSE_sniffio_1_3_1_Apache_Software_License-MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.snowballstemmer.3.0.1(BSD License).txt → LICENSE_snowballstemmer_3_0_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinx-intl.2.3.1(BSD License).txt → LICENSE_sphinx-intl_2_3_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinx-rtd-theme.3.0.2(MIT License).txt → LICENSE_sphinx-rtd-theme_3_0_2_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinx-sitemap.2.6.0(MIT License).txt → LICENSE_sphinx-sitemap_2_7_2_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinx_fontawesome.0.0.6(GNU General Public License v2 (GPLv2)).txt → LICENSE_sphinx_fontawesome_0_0_6_GNU_General_Public_License_v2-GPLv2.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-devhelp.2.0.0(BSD License).txt → LICENSE_sphinxcontrib-applehelp_2_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-htmlhelp.2.1.0(BSD License).txt → LICENSE_sphinxcontrib-devhelp_2_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-jquery.4.1(BSD License).txt → LICENSE_sphinxcontrib-htmlhelp_2_1_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-qthelp.2.0.0(BSD License).txt → LICENSE_sphinxcontrib-jquery_4_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-jsmath.1.0.1(BSD License).txt → LICENSE_sphinxcontrib-jsmath_1_0_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sphinxcontrib-serializinghtml.2.0.0(BSD License).txt → LICENSE_sphinxcontrib-qthelp_2_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tokenizers.0.21.1(Apache Software License).txt → LICENSE_sphinxcontrib-serializinghtml_2_0_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.sse-starlette.2.3.4(BSD License).txt → LICENSE_sse-starlette_2_3_6_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.starlette.0.46.2(BSD License).txt → LICENSE_starlette_0_46_2_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tabulate.0.9.0(MIT License).txt → LICENSE_tabulate_0_9_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tiktoken.0.9.0(MIT License).txt → LICENSE_tiktoken_0_9_0_MIT_License-Copyright-c-2022_OpenAI-Shantanu_Jain-Permission_is_hereby_granted-free_of_charge-to_any_pers.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tomli.2.2.1(MIT License).txt → LICENSE_tomli_2_2_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tqdm.4.67.1(MIT License; Mozilla Public License 2.0 (MPL 2.0)).txt → LICENSE_tqdm_4_67_1_MIT_License-Mozilla_Public_License_2_0-MPL_2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.twine.6.1.0(Apache Software License).txt → LICENSE_twine_6_1_0_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.typing_extensions.4.13.2(UNKNOWN).txt → LICENSE_typing_extensions_4_14_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tzdata.2025.2(Apache Software License).txt → LICENSE_tzdata_2025_2_Apache_Software_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.tzlocal.5.3.1(MIT License).txt → LICENSE_tzlocal_5_3_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.uritemplate.4.1.1(Apache Software License; BSD License).txt → LICENSE_uritemplate_4_2_0_BSD_3-Clause_OR_Apache-2_0.txt} +0 -0
- /cmdbox/licenses/{LICENSE.urllib3.2.4.0(UNKNOWN).txt → LICENSE_urllib3_2_5_0_UNKNOWN.txt} +0 -0
- /cmdbox/licenses/{LICENSE.uvicorn.0.34.2(BSD License).txt → LICENSE_uvicorn_0_35_0_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.watchfiles.1.0.5(MIT License).txt → LICENSE_watchfiles_1_1_0_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.wcwidth.0.2.13(MIT License).txt → LICENSE_wcwidth_0_2_13_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.websockets.15.0.1(BSD License).txt → LICENSE_websockets_15_0_1_BSD_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.wheel.0.45.1(MIT License).txt → LICENSE_wheel_0_45_1_MIT_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.zope.event.5.0(Zope Public License).txt → LICENSE_zope_event_5_1_Zope_Public_License.txt} +0 -0
- /cmdbox/licenses/{LICENSE.zope.interface.7.2(Zope Public License).txt → LICENSE_zope_interface_7_2_Zope_Public_License.txt} +0 -0
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/LICENSE +0 -0
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/WHEEL +0 -0
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/entry_points.txt +0 -0
- {cmdbox-0.6.0.3.dist-info → cmdbox-0.6.1.dist-info}/top_level.txt +0 -0
cmdbox/app/mcp.py
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
from cmdbox.app import common, options
|
|
2
|
+
from cmdbox.app.options import Options
|
|
3
|
+
from cmdbox.app.auth import signin
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Callable, List, Dict, Any, Tuple
|
|
6
|
+
import argparse
|
|
7
|
+
import logging
|
|
8
|
+
import locale
|
|
9
|
+
import json
|
|
10
|
+
import time
|
|
11
|
+
import re
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Mcp:
|
|
16
|
+
default_host:str = os.environ.get('REDIS_HOST', 'localhost')
|
|
17
|
+
default_port:int = int(os.environ.get('REDIS_PORT', '6379'))
|
|
18
|
+
default_pass:str = os.environ.get('REDIS_PASSWORD', 'password')
|
|
19
|
+
default_svname:str = os.environ.get('SVNAME', 'server')
|
|
20
|
+
|
|
21
|
+
def __init__(self, logger:logging.Logger, data:Path, sign:signin.Signin, appcls=None, ver=None,):
|
|
22
|
+
"""
|
|
23
|
+
MCP (Multi-Channel Protocol) クラスの初期化
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
logger (logging.Logger): ロガー
|
|
27
|
+
data (Path): データのパス
|
|
28
|
+
sign (signin.Signin): サインインオブジェクト
|
|
29
|
+
appcls (type, optional): アプリケーションクラス. Defaults to None.
|
|
30
|
+
ver (module, optional): バージョンモジュール. Defaults to None.
|
|
31
|
+
"""
|
|
32
|
+
self.logger = logger
|
|
33
|
+
self.data = data
|
|
34
|
+
self.appcls = appcls
|
|
35
|
+
self.ver = ver
|
|
36
|
+
self.signin = sign
|
|
37
|
+
|
|
38
|
+
def create_mcpserver(self, args:argparse.Namespace) -> Any:
|
|
39
|
+
"""
|
|
40
|
+
mcpserverを作成します
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
args (argparse.Namespace): 引数
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Any: FastMCP
|
|
47
|
+
"""
|
|
48
|
+
from fastmcp import FastMCP
|
|
49
|
+
from fastmcp.server.auth import BearerAuthProvider
|
|
50
|
+
cls = self.signin.__class__
|
|
51
|
+
publickey_str = cls.verify_jwt_publickey_str if hasattr(cls, 'verify_jwt_publickey_str') else None
|
|
52
|
+
issuer = cls.verify_jwt_issuer if hasattr(cls, 'verify_jwt_issuer') else None
|
|
53
|
+
audience = cls.verify_jwt_audience if hasattr(cls, 'verify_jwt_audience') else None
|
|
54
|
+
if publickey_str is not None and issuer is not None and audience is not None:
|
|
55
|
+
self.logger.info(f"Using BearerAuthProvider with public key, issuer: {issuer}, audience: {audience}")
|
|
56
|
+
auth = BearerAuthProvider(
|
|
57
|
+
public_key=publickey_str,
|
|
58
|
+
issuer=issuer,
|
|
59
|
+
audience=audience
|
|
60
|
+
)
|
|
61
|
+
mcp = FastMCP(name=self.ver.__appid__, auth=auth)
|
|
62
|
+
else:
|
|
63
|
+
self.logger.info(f"Using BearerAuthProvider without public key, issuer, or audience.")
|
|
64
|
+
mcp = FastMCP(name=self.ver.__appid__)
|
|
65
|
+
return mcp
|
|
66
|
+
|
|
67
|
+
def create_session_service(self, args:argparse.Namespace) -> Any:
|
|
68
|
+
"""
|
|
69
|
+
セッションサービスを作成します
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
args (argparse.Namespace): 引数
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
BaseSessionService: セッションサービス
|
|
76
|
+
"""
|
|
77
|
+
from google.adk.events import Event
|
|
78
|
+
from google.adk.sessions import DatabaseSessionService, InMemorySessionService, session
|
|
79
|
+
from typing_extensions import override
|
|
80
|
+
if hasattr(args, 'agent_session_dburl') and args.agent_session_dburl is not None:
|
|
81
|
+
class _DatabaseSessionService(DatabaseSessionService):
|
|
82
|
+
@override
|
|
83
|
+
async def append_event(self, session: session.Session, event: Event) -> Event:
|
|
84
|
+
# 永続化されるセッションには <important> タグを含めない
|
|
85
|
+
bk_parts = event.content.parts.copy()
|
|
86
|
+
for part in event.content.parts:
|
|
87
|
+
if not part.text: continue
|
|
88
|
+
part.text = re.sub(r"<important>.*</important>", "", part.text)
|
|
89
|
+
for part in bk_parts:
|
|
90
|
+
if not part.text: continue
|
|
91
|
+
part.text = part.text.replace("<important>", "").replace("</important>", "")
|
|
92
|
+
ret = await super().append_event(session, event)
|
|
93
|
+
ret.content.parts = bk_parts
|
|
94
|
+
return ret
|
|
95
|
+
dss = _DatabaseSessionService(db_url=args.agent_session_dburl)
|
|
96
|
+
#dss.db_engine.echo = True
|
|
97
|
+
return dss
|
|
98
|
+
else:
|
|
99
|
+
return InMemorySessionService()
|
|
100
|
+
|
|
101
|
+
def create_agent(self, logger:logging.Logger, args:argparse.Namespace, tools:List[Callable]) -> Any:
|
|
102
|
+
"""
|
|
103
|
+
エージェントを作成します
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
logger (logging.Logger): ロガー
|
|
107
|
+
args (argparse.Namespace): 引数
|
|
108
|
+
tools (List[Callable]): 関数
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Agent: エージェント
|
|
112
|
+
"""
|
|
113
|
+
if logger.level == logging.DEBUG:
|
|
114
|
+
logger.debug(f"create_agent processing..")
|
|
115
|
+
language, _ = locale.getlocale()
|
|
116
|
+
is_japan = language.find('Japan') >= 0 or language.find('ja_JP') >= 0
|
|
117
|
+
description = f"{self.ver.__appid__}に登録されているコマンド提供"
|
|
118
|
+
instruction = f"あなたはコマンドの意味を熟知しているエキスパートです。" + \
|
|
119
|
+
f"ユーザーがコマンドを実行したいとき、あなたは以下の手順に従ってコマンドを確実に実行してください。\n" + \
|
|
120
|
+
f"1. ユーザーのクエリからが実行したいコマンドを特定します。\n" + \
|
|
121
|
+
f"2. コマンド実行に必要なパラメータのなかで、ユーザーのクエリから取得できないものは、コマンド定義にあるデフォルト値を指定して実行してください。\n" + \
|
|
122
|
+
f"3. もしエラーが発生した場合は、ユーザーにコマンド名とパラメータとエラー内容を提示してください。\n"
|
|
123
|
+
|
|
124
|
+
description = description if is_japan else \
|
|
125
|
+
f"Command offer registered in {self.ver.__appid__}."
|
|
126
|
+
instruction = instruction if is_japan else \
|
|
127
|
+
f"You are the expert who knows what the commands mean." + \
|
|
128
|
+
f"When a user wants to execute a command, you follow these steps to ensure that the command is executed.\n" + \
|
|
129
|
+
f"1. Identify the command you want to execute from the user's query.\n" + \
|
|
130
|
+
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" + \
|
|
131
|
+
f"3. If an error occurs, provide the user with the command name, parameters, and error description.\n"
|
|
132
|
+
|
|
133
|
+
description = args.agent_description if args.agent_description else description
|
|
134
|
+
instruction = args.agent_instruction if args.agent_instruction else instruction
|
|
135
|
+
if logger.level == logging.DEBUG:
|
|
136
|
+
logger.debug(f"google-adk loading..")
|
|
137
|
+
from google.adk.agents import Agent
|
|
138
|
+
if logger.level == logging.DEBUG:
|
|
139
|
+
logger.debug(f"litellm loading..")
|
|
140
|
+
from google.adk.models.lite_llm import LiteLlm
|
|
141
|
+
# loggerの初期化
|
|
142
|
+
common.reset_logger("LiteLLM Proxy")
|
|
143
|
+
common.reset_logger("LiteLLM Router")
|
|
144
|
+
common.reset_logger("LiteLLM")
|
|
145
|
+
if args.llmprov == 'openai':
|
|
146
|
+
if args.llmmodel is None: raise ValueError("llmmodel is required.")
|
|
147
|
+
if args.llmapikey is None: raise ValueError("llmapikey is required.")
|
|
148
|
+
agent = Agent(
|
|
149
|
+
name=args.agent_name,
|
|
150
|
+
model=LiteLlm(
|
|
151
|
+
model=args.llmmodel,
|
|
152
|
+
api_key=args.llmapikey,
|
|
153
|
+
endpoint=args.llmendpoint,
|
|
154
|
+
),
|
|
155
|
+
description=description,
|
|
156
|
+
instruction=instruction,
|
|
157
|
+
tools=tools,
|
|
158
|
+
)
|
|
159
|
+
elif args.llmprov == 'azureopenai':
|
|
160
|
+
if args.llmmodel is None: raise ValueError("llmmodel is required.")
|
|
161
|
+
if args.llmendpoint is None: raise ValueError("llmendpoint is required.")
|
|
162
|
+
if args.llmapikey is None: raise ValueError("llmapikey is required.")
|
|
163
|
+
if args.llmapiversion is None: raise ValueError("llmapiversion is required.")
|
|
164
|
+
agent = Agent(
|
|
165
|
+
name=args.agent_name,
|
|
166
|
+
model=LiteLlm(
|
|
167
|
+
model=args.llmmodel,
|
|
168
|
+
api_key=args.llmapikey,
|
|
169
|
+
endpoint=args.llmendpoint,
|
|
170
|
+
api_version=args.llmapiversion,
|
|
171
|
+
),
|
|
172
|
+
description=description,
|
|
173
|
+
instruction=instruction,
|
|
174
|
+
tools=tools,
|
|
175
|
+
)
|
|
176
|
+
elif args.llmprov == 'vertexai':
|
|
177
|
+
if args.llmmodel is None: raise ValueError("llmmodel is required.")
|
|
178
|
+
if args.llmlocation is None: raise ValueError("llmlocation is required.")
|
|
179
|
+
if args.llmsvaccountfile is not None:
|
|
180
|
+
with open(args.llmsvaccountfile, "r", encoding="utf-8") as f:
|
|
181
|
+
vertex_credentials = json.load(f)
|
|
182
|
+
elif args.llmprojectid is None: raise ValueError("llmprojectid is required.")
|
|
183
|
+
agent = Agent(
|
|
184
|
+
name=args.agent_name,
|
|
185
|
+
model=LiteLlm(
|
|
186
|
+
model=args.llmmodel,
|
|
187
|
+
#vertex_project=args.llmprojectid,
|
|
188
|
+
vertex_credentials=vertex_credentials,
|
|
189
|
+
vertex_location=args.llmlocation,
|
|
190
|
+
#seed=args.llmseed,
|
|
191
|
+
#temperature=args.llmtemperature,
|
|
192
|
+
),
|
|
193
|
+
description=description,
|
|
194
|
+
instruction=instruction,
|
|
195
|
+
tools=tools,
|
|
196
|
+
)
|
|
197
|
+
elif args.llmprov == 'ollama':
|
|
198
|
+
if args.llmmodel is None: raise ValueError("llmmodel is required.")
|
|
199
|
+
if args.llmendpoint is None: raise ValueError("llmendpoint is required.")
|
|
200
|
+
agent = Agent(
|
|
201
|
+
name=args.agent_name,
|
|
202
|
+
model=LiteLlm(
|
|
203
|
+
model=f"ollama/{args.llmmodel}",
|
|
204
|
+
api_base=args.llmendpoint,
|
|
205
|
+
temperature=args.llmtemperature,
|
|
206
|
+
stream=True
|
|
207
|
+
),
|
|
208
|
+
description=description,
|
|
209
|
+
instruction=instruction,
|
|
210
|
+
tools=tools,
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
raise ValueError("llmprov is required.")
|
|
214
|
+
if logger.level == logging.DEBUG:
|
|
215
|
+
logger.debug(f"create_agent complate.")
|
|
216
|
+
return agent
|
|
217
|
+
|
|
218
|
+
def create_runner(self, logger:logging.Logger, args:argparse.Namespace, session_service, agent) -> Any:
|
|
219
|
+
"""
|
|
220
|
+
ランナーを作成します
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
logger (logging.Logger): ロガー
|
|
224
|
+
args (argparse.Namespace): 引数
|
|
225
|
+
session_service (BaseSessionService): セッションサービス
|
|
226
|
+
agent (Agent): エージェント
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Runner: ランナー
|
|
230
|
+
"""
|
|
231
|
+
from google.adk.runners import Runner
|
|
232
|
+
return Runner(
|
|
233
|
+
app_name=self.ver.__appid__,
|
|
234
|
+
agent=agent,
|
|
235
|
+
session_service=session_service,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def init_agent_runner(self, logger:logging.Logger, args:argparse.Namespace) -> Tuple[Any, Any]:
|
|
239
|
+
"""
|
|
240
|
+
エージェントの初期化を行います
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
logger (logging.Logger): ロガー
|
|
244
|
+
args (argparse.Namespace): 引数
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Tuple[Any, Any]: ランナーとFastMCP
|
|
248
|
+
"""
|
|
249
|
+
if logger.level == logging.DEBUG:
|
|
250
|
+
logger.debug(f"init_agent_runner processing..")
|
|
251
|
+
# loggerの初期化
|
|
252
|
+
common.reset_logger("httpx")
|
|
253
|
+
common.reset_logger("google_adk.google.adk.sessions.database_session_service")
|
|
254
|
+
common.reset_logger("mcp.server.streamable_http_manager")
|
|
255
|
+
# モジュールインポート
|
|
256
|
+
from fastmcp import FastMCP
|
|
257
|
+
from google.adk.sessions import BaseSessionService
|
|
258
|
+
mcp:FastMCP = self.create_mcpserver(args)
|
|
259
|
+
session_service:BaseSessionService = self.create_session_service(args)
|
|
260
|
+
options = Options.getInstance()
|
|
261
|
+
tools:Callable[[logging.Logger, argparse.Namespace, float, Dict], Tuple[int, Dict[str, Any], Any]] = []
|
|
262
|
+
|
|
263
|
+
def _ds(d:str) -> str:
|
|
264
|
+
return f'"{d}"' if d is not None else 'None'
|
|
265
|
+
def _t2s(o:Dict[str, Any], req=True) -> str:
|
|
266
|
+
t, m, d, r = o["type"], o["multi"], o["default"], o["required"]
|
|
267
|
+
if t == Options.T_BOOL: return ("List[bool]=[]" if m else f"bool={d}") if req else ("List[bool]" if m else f"bool")
|
|
268
|
+
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")
|
|
269
|
+
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")
|
|
270
|
+
if t == Options.T_DICT: return ("List[dict]=[]" if m else f"dict={d}") if req else ("List[dict]" if m else f"dict")
|
|
271
|
+
if t == Options.T_DIR or t == Options.T_FILE:
|
|
272
|
+
if d is not None: d = str(d).replace('\\', '/')
|
|
273
|
+
return ("List[str]=[]" if m else f"str={_ds(d)}") if req else ("List[str]" if m else f"str")
|
|
274
|
+
if t == Options.T_FLOAT: return ("List[float]=[]" if m else f"float={d}") if req else ("List[float]" if m else f"float")
|
|
275
|
+
if t == Options.T_INT: return ("List[int]=[]" if m else f"int={d}") if req else ("List[int]" if m else f"int")
|
|
276
|
+
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")
|
|
277
|
+
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")
|
|
278
|
+
raise ValueError(f"Unknown type: {t} for option {o['opt']}")
|
|
279
|
+
def _arg(o:Dict[str, Any], is_japan) -> str:
|
|
280
|
+
t, d = o["type"], o["default"]
|
|
281
|
+
s = f' {o["opt"]}:'
|
|
282
|
+
if t == Options.T_DIR or t == Options.T_FILE:
|
|
283
|
+
d = str(d).replace("\\", "/")
|
|
284
|
+
s += f'{_t2s(o, False)}={d}:'
|
|
285
|
+
#s += f'Optional[{_t2s(o, False)}]={d}:'
|
|
286
|
+
s += f'{o["discription_ja"] if is_japan else o["discription_en"]}'
|
|
287
|
+
return s
|
|
288
|
+
def _coercion(a:argparse.Namespace, key:str, dval) -> str:
|
|
289
|
+
dval = f'opt["{key}"] if "{key}" in opt else ' + f'"{dval}"' if isinstance(dval, str) else dval
|
|
290
|
+
aval = args.__dict__[key] if hasattr(args, key) and args.__dict__[key] else None
|
|
291
|
+
aval = f'"{aval}"' if isinstance(aval, str) else aval
|
|
292
|
+
ret = f'opt["{key}"] = {aval}' if aval is not None else f'opt["{key}"] = {dval}'
|
|
293
|
+
return ret
|
|
294
|
+
language, _ = locale.getlocale()
|
|
295
|
+
is_japan = language.find('Japan') >= 0 or language.find('ja_JP') >= 0
|
|
296
|
+
for mode in options.get_mode_keys():
|
|
297
|
+
for cmd in options.get_cmd_keys(mode):
|
|
298
|
+
if not options.get_cmd_attr(mode, cmd, 'use_agent'):
|
|
299
|
+
continue
|
|
300
|
+
discription = options.get_cmd_attr(mode, cmd, 'discription_ja' if is_japan else 'discription_en')
|
|
301
|
+
choices = options.get_cmd_choices(mode, cmd, False)
|
|
302
|
+
if len([opt for opt in choices if 'opt' in opt and opt['opt'] == 'signin_file']) <= 0:
|
|
303
|
+
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,
|
|
304
|
+
discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
|
|
305
|
+
discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),)
|
|
306
|
+
fn = f"{mode}_{cmd}"
|
|
307
|
+
func_txt = f'def {fn}(' + ", ".join([f'{o["opt"]}:{_t2s(o, False)}' for o in choices]) + '):\n'
|
|
308
|
+
func_txt += f' """\n'
|
|
309
|
+
func_txt += f' {discription}\n'
|
|
310
|
+
func_txt += f' Args:\n'
|
|
311
|
+
func_txt += "\n".join([_arg(o, is_japan) for o in choices])
|
|
312
|
+
func_txt += f'\n'
|
|
313
|
+
func_txt += f' Returns:\n'
|
|
314
|
+
func_txt += f' Dict[str, Any]:{"処理結果" if is_japan else "Processing Result"}\n'
|
|
315
|
+
func_txt += f' """\n'
|
|
316
|
+
func_txt += f' scope = signin.get_request_scope()\n'
|
|
317
|
+
func_txt += f' logger = common.default_logger()\n'
|
|
318
|
+
func_txt += f' opt = dict()\n'
|
|
319
|
+
func_txt += f' opt["mode"] = "{mode}"\n'
|
|
320
|
+
func_txt += f' opt["cmd"] = "{cmd}"\n'
|
|
321
|
+
func_txt += f' opt["data"] = opt["data"] if hasattr(opt, "data") else common.HOME_DIR / ".{self.ver.__appid__}"\n'
|
|
322
|
+
func_txt += f' opt["format"] = False\n'
|
|
323
|
+
func_txt += f' opt["output_json"] = None\n'
|
|
324
|
+
func_txt += f' opt["output_json_append"] = False\n'
|
|
325
|
+
func_txt += f' opt["debug"] = logger.level == logging.DEBUG\n'
|
|
326
|
+
func_txt += '\n'.join([f' opt["{o["opt"]}"] = {o["opt"]}' for o in choices])+'\n'
|
|
327
|
+
func_txt += f' {_coercion(args, "host", self.default_host)}\n'
|
|
328
|
+
func_txt += f' {_coercion(args, "port", self.default_port)}\n'
|
|
329
|
+
func_txt += f' {_coercion(args, "password", self.default_pass)}\n'
|
|
330
|
+
func_txt += f' {_coercion(args, "svname", self.default_svname)}\n'
|
|
331
|
+
func_txt += f' {_coercion(args, "retry_count", 3)}\n'
|
|
332
|
+
func_txt += f' {_coercion(args, "retry_interval", 3)}\n'
|
|
333
|
+
func_txt += f' {_coercion(args, "timeout", 15)}\n'
|
|
334
|
+
func_txt += f' {_coercion(args, "output_json", None)}\n'
|
|
335
|
+
func_txt += f' {_coercion(args, "output_json_append", False)}\n'
|
|
336
|
+
func_txt += f' {_coercion(args, "stdout_log", False)}\n'
|
|
337
|
+
func_txt += f' {_coercion(args, "capture_stdout", False)}\n'
|
|
338
|
+
func_txt += f' {_coercion(args, "capture_maxsize", 1024*1024)}\n'
|
|
339
|
+
func_txt += f' {_coercion(args, "tag", None)}\n'
|
|
340
|
+
func_txt += f' {_coercion(args, "clmsg_id", None)}\n'
|
|
341
|
+
func_txt += f' opt["signin_file"] = signin_file if signin_file else ".{self.ver.__appid__}/user_list.yml"\n'
|
|
342
|
+
func_txt += f' args = argparse.Namespace(**opt)\n'
|
|
343
|
+
func_txt += f' signin_data = signin.Signin.load_signin_file(args.signin_file)\n'
|
|
344
|
+
func_txt += f' req = scope["req"] if scope["req"] is not None else scope["websocket"]\n'
|
|
345
|
+
func_txt += f' sign = signin.Signin._check_signin(req, scope["res"], signin_data, logger)\n'
|
|
346
|
+
func_txt += f' if sign is not None:\n'
|
|
347
|
+
func_txt += f' logger.warning("Unable to execute command because authentication information cannot be obtained")\n'
|
|
348
|
+
func_txt += f' return dict(warn="Unable to execute command because authentication information cannot be obtained")\n'
|
|
349
|
+
func_txt += f' groups = req.session["signin"]["groups"]\n'
|
|
350
|
+
func_txt += f' logger.info("Call agent tool `{mode}_{cmd}`:user="+str(req.session["signin"]["name"])+" groups="+str(groups)+" args="+str(args))\n'
|
|
351
|
+
func_txt += f' if not signin.Signin._check_cmd(signin_data, groups, "{mode}", "{cmd}", logger):\n'
|
|
352
|
+
func_txt += f' logger.warning("You do not have permission to execute this command.")\n'
|
|
353
|
+
func_txt += f' return dict(warn="You do not have permission to execute this command.")\n'
|
|
354
|
+
func_txt += f' feat = Options.getInstance().get_cmd_attr("{mode}", "{cmd}", "feature")\n'
|
|
355
|
+
func_txt += f' try:\n'
|
|
356
|
+
func_txt += f' st, ret, _ = feat.apprun(logger, args, time.perf_counter(), [])\n'
|
|
357
|
+
func_txt += f' return ret\n'
|
|
358
|
+
func_txt += f' except Exception as e:\n'
|
|
359
|
+
func_txt += f' logger.error("Error occurs when tool is executed:", exc_info=True)\n'
|
|
360
|
+
func_txt += f' raise e\n'
|
|
361
|
+
func_txt += f'tools.append({fn})\n'
|
|
362
|
+
if logger.level == logging.DEBUG:
|
|
363
|
+
logger.debug(f"generating agent tool: {fn}")
|
|
364
|
+
|
|
365
|
+
exec(func_txt,
|
|
366
|
+
dict(time=time,List=List, argparse=argparse, common=common, Options=Options, logging=logging, signin=signin,),
|
|
367
|
+
dict(tools=tools, mcp=mcp))
|
|
368
|
+
exec(f"@mcp.tool\n{func_txt}",
|
|
369
|
+
dict(time=time,List=List, argparse=argparse, common=common, Options=Options, logging=logging, signin=signin,),
|
|
370
|
+
dict(tools=[], mcp=mcp))
|
|
371
|
+
root_agent = self.create_agent(logger, args, tools)
|
|
372
|
+
runner = self.create_runner(logger, args, session_service, root_agent)
|
|
373
|
+
if logger.level == logging.DEBUG:
|
|
374
|
+
logger.debug(f"init_agent_runner complate.")
|
|
375
|
+
return runner, mcp
|
cmdbox/app/options.py
CHANGED
|
@@ -295,12 +295,18 @@ class Options:
|
|
|
295
295
|
discription_ja="クライアントのメッセージIDを指定します。省略した場合はuuid4で生成されます。",
|
|
296
296
|
discription_en="Specifies the message ID of the client. If omitted, uuid4 will be generated.",
|
|
297
297
|
choice=None)
|
|
298
|
+
self._options["description"] = dict(
|
|
299
|
+
type=Options.T_TEXT, default=None, required=False, multi=False, hide=True,
|
|
300
|
+
discription_ja="このコマンド登録の説明文を指定します。Agentがこのコマンドの用途を理解するのに使用します。",
|
|
301
|
+
discription_en="Specifies a description of this command registration, used to help the Agent understand the use of this command.",
|
|
302
|
+
choice=None)
|
|
298
303
|
|
|
299
304
|
def init_debugoption(self):
|
|
300
305
|
# デバックオプションを追加
|
|
301
306
|
self._options["debug"]["opt"] = "debug"
|
|
302
307
|
self._options["tag"]["opt"] = "tag"
|
|
303
308
|
self._options["clmsg_id"]["opt"] = "clmsg_id"
|
|
309
|
+
self._options["description"]["opt"] = "description"
|
|
304
310
|
for key, mode in self._options["cmd"].items():
|
|
305
311
|
if type(mode) is not dict:
|
|
306
312
|
continue
|
|
@@ -315,6 +321,8 @@ class Options:
|
|
|
315
321
|
c["choice"].append(self._options["tag"])
|
|
316
322
|
if "clmsg_id" not in [_o['opt'] for _o in c["choice"]]:
|
|
317
323
|
c["choice"].append(self._options["clmsg_id"])
|
|
324
|
+
if "description" not in [_o['opt'] for _o in c["choice"]]:
|
|
325
|
+
c["choice"].append(self._options["description"])
|
|
318
326
|
if c["opt"] not in [_o['opt'] for _o in self._options["cmd"]["choice"]]:
|
|
319
327
|
self._options["cmd"]["choice"] += [c]
|
|
320
328
|
self._options["mode"][key] = mode
|
|
@@ -824,11 +832,13 @@ class Options:
|
|
|
824
832
|
if hasattr(arg, 'redis_password'): opt['password'] = arg.redis_password
|
|
825
833
|
if hasattr(arg, 'svname'): opt['svname'] = arg.svname
|
|
826
834
|
if hasattr(arg, 'clmsg_id'): opt['clmsg_id'] = arg.clmsg_id
|
|
835
|
+
if hasattr(arg, 'client_only'): opt['client_only'] = arg.client_only
|
|
827
836
|
elif isinstance(arg, web.Web):
|
|
828
837
|
opt['host'] = arg.redis_host
|
|
829
838
|
opt['port'] = arg.redis_port
|
|
830
839
|
opt['password'] = arg.redis_password
|
|
831
840
|
opt['svname'] = arg.svname
|
|
841
|
+
opt['client_only'] = arg.client_only
|
|
832
842
|
elif isinstance(arg, feature.Feature):
|
|
833
843
|
func_feature = arg
|
|
834
844
|
opt['clmsg_src'] = func_feature.__class__.__name__
|
cmdbox/app/web.py
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
from cmdbox.app import common, options
|
|
2
|
-
from cmdbox.app.auth import signin, signin_saml
|
|
3
2
|
from cmdbox.app.commons import module
|
|
4
3
|
from fastapi import FastAPI, Request, Response
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
from starlette.applications import Starlette
|
|
7
6
|
from starlette.middleware.sessions import SessionMiddleware
|
|
8
|
-
from starlette.routing import Mount
|
|
9
7
|
from typing import Any, Dict, List
|
|
10
8
|
from uvicorn.config import Config
|
|
11
9
|
import asyncio
|
|
@@ -13,6 +11,7 @@ import copy
|
|
|
13
11
|
import ctypes
|
|
14
12
|
import datetime
|
|
15
13
|
import gevent
|
|
14
|
+
import jwt
|
|
16
15
|
import logging
|
|
17
16
|
import multiprocessing
|
|
18
17
|
import os
|
|
@@ -20,7 +19,6 @@ import platform
|
|
|
20
19
|
import requests
|
|
21
20
|
import queue
|
|
22
21
|
import signal
|
|
23
|
-
import string
|
|
24
22
|
import time
|
|
25
23
|
import threading
|
|
26
24
|
import traceback
|
|
@@ -30,10 +28,10 @@ import webbrowser
|
|
|
30
28
|
|
|
31
29
|
class Web:
|
|
32
30
|
def __init__(self, logger:logging.Logger, data:Path, appcls=None, ver=None,
|
|
33
|
-
redis_host:str
|
|
31
|
+
redis_host:str="localhost", redis_port:int=6379, redis_password:str=None, svname:str='server',
|
|
34
32
|
client_only:bool=False, doc_root:Path=None, gui_html:str=None, filer_html:str=None, result_html:str=None, users_html:str=None,
|
|
35
33
|
audit_html:str=None, agent_html:str=None, assets:List[str]=None, signin_html:str=None, signin_file:str=None, gui_mode:bool=False,
|
|
36
|
-
web_features_packages:List[str]=None, web_features_prefix:List[str]=
|
|
34
|
+
web_features_packages:List[str]=None, web_features_prefix:List[str]=[]):
|
|
37
35
|
"""
|
|
38
36
|
cmdboxクライアント側のwebapiサービス
|
|
39
37
|
|
|
@@ -122,7 +120,8 @@ class Web:
|
|
|
122
120
|
self.cb_queue = queue.Queue(1000)
|
|
123
121
|
self.options = options.Options.getInstance()
|
|
124
122
|
self.webcap_client = requests.Session()
|
|
125
|
-
|
|
123
|
+
from cmdbox.app.auth import signin, signin_saml
|
|
124
|
+
signin_file_data = signin.Signin.load_signin_file(self.signin_file, self=self)
|
|
126
125
|
self.signin = signin.Signin(self.logger, self.signin_file, signin_file_data, self.appcls, self.ver)
|
|
127
126
|
self.signin_saml = signin_saml.SigninSAML(self.logger, self.signin_file, signin_file_data, self.appcls, self.ver)
|
|
128
127
|
|
|
@@ -249,7 +248,25 @@ class Web:
|
|
|
249
248
|
for u in copy.deepcopy(signin_data['users']):
|
|
250
249
|
u['password'] = '********'
|
|
251
250
|
if 'apikeys' in u:
|
|
252
|
-
|
|
251
|
+
for an, ak in u['apikeys'].items():
|
|
252
|
+
exp = '-'
|
|
253
|
+
try:
|
|
254
|
+
cls = self.signin.__class__
|
|
255
|
+
publickey = None
|
|
256
|
+
if cls.verify_jwt_certificate is not None:
|
|
257
|
+
publickey = cls.verify_jwt_certificate.public_key()
|
|
258
|
+
if publickey is None and cls.verify_jwt_publickey is not None:
|
|
259
|
+
publickey = cls.verify_jwt_publickey
|
|
260
|
+
t = jwt.decode(ak, publickey, algorithms=[cls.verify_jwt_algorithm],
|
|
261
|
+
issuer=cls.verify_jwt_issuer, audience=cls.verify_jwt_audience,
|
|
262
|
+
options={'verify_iss': cls.verify_jwt_issuer is not None,
|
|
263
|
+
'verify_aud': cls.verify_jwt_audience is not None})
|
|
264
|
+
exp = datetime.datetime.fromtimestamp(t['exp']).strftime('%Y-%m-%d %H:%M:%S')
|
|
265
|
+
u['apikeys'][an] = (ak, exp, '-')
|
|
266
|
+
except jwt.exceptions.InvalidTokenError as e:
|
|
267
|
+
u['apikeys'][an] = (ak, '-', str(e))
|
|
268
|
+
except Exception as e:
|
|
269
|
+
u['apikeys'][an] = (ak, '-', '-')
|
|
253
270
|
if u['name'] == name:
|
|
254
271
|
return [u]
|
|
255
272
|
signin_last = self.user_data(None, u['uid'], u['name'], 'signin', 'last_update')
|
|
@@ -291,10 +308,19 @@ class Web:
|
|
|
291
308
|
if user['apikey_name'] in u['apikeys']:
|
|
292
309
|
raise ValueError(f"ApiKey name is already exists. ({user})")
|
|
293
310
|
apikey = common.random_string(64)
|
|
294
|
-
u['apikeys'][user['apikey_name']] =
|
|
311
|
+
u['apikeys'][user['apikey_name']] = apikey
|
|
312
|
+
if signin_data['apikey']['gen_jwt']['enabled']:
|
|
313
|
+
cls = self.signin.__class__
|
|
314
|
+
claims = cls.gen_jwt_claims.copy() if cls.gen_jwt_claims is not None else dict()
|
|
315
|
+
claims['exp'] = int(time.time()) + int(claims.get('exp', 3600))
|
|
316
|
+
claims['uid'] = u['uid']
|
|
317
|
+
claims['name'] = u['name']
|
|
318
|
+
claims['groups'] = u['groups']
|
|
319
|
+
claims['email'] = u['email']
|
|
320
|
+
claims['apikey_name'] = user['apikey_name']
|
|
321
|
+
apikey = jwt.encode(claims, cls.gen_jwt_privatekey, algorithm=cls.gen_jwt_algorithm)
|
|
322
|
+
u['apikeys'][user['apikey_name']] = apikey
|
|
295
323
|
|
|
296
|
-
if self.signin_file is None:
|
|
297
|
-
raise ValueError(f"signin_file is None.")
|
|
298
324
|
if self.logger.level == logging.DEBUG:
|
|
299
325
|
self.logger.debug(f"apikey_add: {user} -> {self.signin_file}")
|
|
300
326
|
common.save_yml(self.signin_file, signin_data)
|
|
@@ -679,7 +705,7 @@ class Web:
|
|
|
679
705
|
ssl_cert:Path=None, ssl_key:Path=None, ssl_keypass:str=None, ssl_ca_certs:Path=None,
|
|
680
706
|
session_domain:str=None, session_path:str='/', session_secure:bool=False, session_timeout:int=900, outputs_key:List[str]=[],
|
|
681
707
|
guvicorn_workers:int=-1, guvicorn_timeout:int=30,
|
|
682
|
-
agent_runner=None, mcp=None,
|
|
708
|
+
agent_runner=None, mcp=None,):
|
|
683
709
|
"""
|
|
684
710
|
Webサーバを起動する
|
|
685
711
|
|
|
@@ -700,8 +726,6 @@ class Web:
|
|
|
700
726
|
guvicorn_timeout (int, optional): Gunicornタイムアウト. Defaults to 30.
|
|
701
727
|
agent_runner (Runner, optional): エージェントランナー. Defaults to None.
|
|
702
728
|
mcp (MCP, optional): MCP. Defaults to None.
|
|
703
|
-
mcp_listen_port (int, optional): MCPリスンポート. Defaults to 9081.
|
|
704
|
-
mcp_ssl_listen_port (int, optional): MCP SSLリスンポート. Defaults to 9443.
|
|
705
729
|
"""
|
|
706
730
|
self.allow_host = allow_host
|
|
707
731
|
self.listen_port = listen_port
|
|
@@ -719,8 +743,6 @@ class Web:
|
|
|
719
743
|
self.guvicorn_timeout = guvicorn_timeout
|
|
720
744
|
self.agent_runner = agent_runner
|
|
721
745
|
self.mcp = mcp
|
|
722
|
-
self.mcp_listen_port = mcp_listen_port
|
|
723
|
-
self.mcp_ssl_listen_port = mcp_ssl_listen_port
|
|
724
746
|
if self.logger.level == logging.DEBUG:
|
|
725
747
|
self.logger.debug(f"web start parameter: allow_host={self.allow_host}")
|
|
726
748
|
self.logger.debug(f"web start parameter: listen_port={self.listen_port}")
|
|
@@ -738,8 +760,6 @@ class Web:
|
|
|
738
760
|
self.logger.debug(f"web start parameter: guvicorn_timeout={self.guvicorn_timeout}")
|
|
739
761
|
self.logger.debug(f"web start parameter: agent_runner={self.agent_runner}")
|
|
740
762
|
self.logger.debug(f"web start parameter: mcp={self.mcp}")
|
|
741
|
-
self.logger.debug(f"web start parameter: mcp_listen_port={self.mcp_listen_port}")
|
|
742
|
-
self.logger.debug(f"web start parameter: mcp_ssl_listen_port={self.mcp_ssl_listen_port}")
|
|
743
763
|
|
|
744
764
|
if self.agent_runner is not None:
|
|
745
765
|
# google.adkが大きいので必要な時にだけ読込む
|
|
@@ -778,7 +798,10 @@ class Web:
|
|
|
778
798
|
sessions = await session_service.list_sessions(app_name=self.ver.__appid__, user_id=user_id)
|
|
779
799
|
ret = []
|
|
780
800
|
for s in sessions.sessions:
|
|
781
|
-
|
|
801
|
+
session = await session_service.get_session(app_name=self.ver.__appid__, user_id=user_id, session_id=s.id)
|
|
802
|
+
if session is None:
|
|
803
|
+
continue
|
|
804
|
+
ret.append(session)
|
|
782
805
|
return ret
|
|
783
806
|
else:
|
|
784
807
|
session = await session_service.get_session(app_name=self.ver.__appid__, user_id=user_id, session_id=session_id)
|
|
@@ -801,17 +824,15 @@ class Web:
|
|
|
801
824
|
return await session_service.delete_session(app_name=self.ver.__appid__, user_id=user_id, session_id=session_id)
|
|
802
825
|
self.delete_agent_session = delete_agent_session
|
|
803
826
|
|
|
804
|
-
|
|
827
|
+
mcp_app:Starlette = None
|
|
805
828
|
if self.mcp is not None:
|
|
806
|
-
# MCPをFastAPIにマウント
|
|
807
829
|
mcp_app:Starlette = self.mcp.streamable_http_app()
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
830
|
+
#mcp_app:Starlette = self.mcp.http_app()
|
|
831
|
+
if mcp_app is not None:
|
|
832
|
+
app = FastAPI(lifespan=mcp_app.lifespan)
|
|
811
833
|
else:
|
|
812
834
|
app = FastAPI()
|
|
813
|
-
|
|
814
|
-
app = FastAPI()
|
|
835
|
+
|
|
815
836
|
@app.middleware("http")
|
|
816
837
|
async def set_context_cookie(req:Request, call_next):
|
|
817
838
|
res:Response = await call_next(req)
|
|
@@ -824,20 +845,13 @@ class Web:
|
|
|
824
845
|
if self.session_secure:
|
|
825
846
|
mwparam['https_only'] = True # セッションハイジャック対策
|
|
826
847
|
app.add_middleware(SessionMiddleware, **mwparam)
|
|
848
|
+
if mcp_app is not None:
|
|
849
|
+
app.mount("/mcpsv", mcp_app, name="mcp")
|
|
850
|
+
self.logger.info(f"mcp server mount: mount_path=/mcpsv app={mcp_app} routes={mcp_app.routes}")
|
|
827
851
|
self.init_webfeatures(app)
|
|
828
852
|
|
|
829
853
|
self.is_running = True
|
|
830
|
-
|
|
831
|
-
http_config = Config(app=app, host=self.allow_host, port=self.listen_port)
|
|
832
|
-
th = ThreadedUvicorn(self.logger, config=http_config,
|
|
833
|
-
guvicorn_config=dict(workers=self.guvicorn_workers, timeout=self.guvicorn_timeout))
|
|
834
|
-
th.start()
|
|
835
|
-
if self.mcp is not None and self.ssl_cert is None and self.ssl_key is None:
|
|
836
|
-
mcp_app:Starlette = self.mcp.streamable_http_app()
|
|
837
|
-
http_config = Config(app=mcp_app, host=self.allow_host, port=self.mcp_listen_port)
|
|
838
|
-
mcp_th = ThreadedUvicorn(self.logger, config=http_config, force_uvicorn=True)
|
|
839
|
-
mcp_th.start()
|
|
840
|
-
browser_port = self.listen_port
|
|
854
|
+
th = None
|
|
841
855
|
th_ssl = None
|
|
842
856
|
if self.ssl_cert is not None and self.ssl_key is not None:
|
|
843
857
|
https_config = Config(app=app, host=self.allow_host, port=self.ssl_listen_port,
|
|
@@ -847,13 +861,12 @@ class Web:
|
|
|
847
861
|
guvicorn_config=dict(workers=self.guvicorn_workers, timeout=self.guvicorn_timeout))
|
|
848
862
|
th_ssl.start()
|
|
849
863
|
browser_port = self.ssl_listen_port
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
mcp_th_ssl.start()
|
|
864
|
+
else:
|
|
865
|
+
http_config = Config(app=app, host=self.allow_host, port=self.listen_port)
|
|
866
|
+
th = ThreadedUvicorn(self.logger, config=http_config,
|
|
867
|
+
guvicorn_config=dict(workers=self.guvicorn_workers, timeout=self.guvicorn_timeout))
|
|
868
|
+
th.start()
|
|
869
|
+
browser_port = self.listen_port
|
|
857
870
|
try:
|
|
858
871
|
if self.gui_mode:
|
|
859
872
|
webbrowser.open(f'http://localhost:{browser_port}/gui')
|
|
@@ -861,21 +874,15 @@ class Web:
|
|
|
861
874
|
f.write(str(os.getpid()))
|
|
862
875
|
while self.is_running:
|
|
863
876
|
gevent.sleep(1)
|
|
864
|
-
th
|
|
865
|
-
|
|
866
|
-
mcp_th.stop()
|
|
877
|
+
if th is not None:
|
|
878
|
+
th.stop()
|
|
867
879
|
if th_ssl is not None:
|
|
868
880
|
th_ssl.stop()
|
|
869
|
-
if self.mcp is not None:
|
|
870
|
-
mcp_th_ssl.stop()
|
|
871
881
|
except KeyboardInterrupt:
|
|
872
|
-
th
|
|
873
|
-
|
|
874
|
-
mcp_th.stop()
|
|
882
|
+
if th is not None:
|
|
883
|
+
th.stop()
|
|
875
884
|
if th_ssl is not None:
|
|
876
885
|
th_ssl.stop()
|
|
877
|
-
if self.mcp is not None:
|
|
878
|
-
mcp_th_ssl.stop()
|
|
879
886
|
|
|
880
887
|
def stop(self):
|
|
881
888
|
"""
|