cmdbox 0.5.4__py3-none-any.whl → 0.6.0.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.

Files changed (148) hide show
  1. cmdbox/app/auth/signin.py +463 -303
  2. cmdbox/app/common.py +51 -3
  3. cmdbox/app/commons/loghandler.py +62 -13
  4. cmdbox/app/edge.py +5 -173
  5. cmdbox/app/edge_tool.py +177 -0
  6. cmdbox/app/feature.py +10 -9
  7. cmdbox/app/features/cli/agent_base.py +479 -0
  8. cmdbox/app/features/cli/audit_base.py +17 -5
  9. cmdbox/app/features/cli/cmdbox_audit_search.py +24 -1
  10. cmdbox/app/features/cli/cmdbox_client_file_download.py +1 -1
  11. cmdbox/app/features/cli/cmdbox_cmd_list.py +105 -0
  12. cmdbox/app/features/cli/cmdbox_cmd_load.py +104 -0
  13. cmdbox/app/features/cli/cmdbox_edge_config.py +2 -2
  14. cmdbox/app/features/cli/cmdbox_edge_start.py +1 -1
  15. cmdbox/app/features/cli/cmdbox_gui_start.py +9 -132
  16. cmdbox/app/features/cli/cmdbox_gui_stop.py +4 -21
  17. cmdbox/app/features/cli/cmdbox_server_start.py +1 -1
  18. cmdbox/app/features/cli/cmdbox_web_apikey_add.py +1 -1
  19. cmdbox/app/features/cli/cmdbox_web_apikey_del.py +1 -1
  20. cmdbox/app/features/cli/cmdbox_web_genpass.py +0 -3
  21. cmdbox/app/features/cli/cmdbox_web_group_add.py +1 -1
  22. cmdbox/app/features/cli/cmdbox_web_group_del.py +1 -1
  23. cmdbox/app/features/cli/cmdbox_web_group_edit.py +1 -1
  24. cmdbox/app/features/cli/cmdbox_web_group_list.py +1 -1
  25. cmdbox/app/features/cli/cmdbox_web_start.py +120 -104
  26. cmdbox/app/features/cli/cmdbox_web_stop.py +1 -1
  27. cmdbox/app/features/cli/cmdbox_web_user_add.py +1 -1
  28. cmdbox/app/features/cli/cmdbox_web_user_del.py +1 -1
  29. cmdbox/app/features/cli/cmdbox_web_user_edit.py +1 -1
  30. cmdbox/app/features/cli/cmdbox_web_user_list.py +1 -1
  31. cmdbox/app/features/web/cmdbox_web_agent.py +262 -0
  32. cmdbox/app/features/web/cmdbox_web_do_signout.py +3 -3
  33. cmdbox/app/features/web/cmdbox_web_exec_cmd.py +8 -3
  34. cmdbox/app/features/web/cmdbox_web_signin.py +5 -4
  35. cmdbox/app/features/web/cmdbox_web_users.py +2 -0
  36. cmdbox/app/options.py +62 -9
  37. cmdbox/app/web.py +139 -15
  38. cmdbox/extensions/features.yml +18 -0
  39. cmdbox/extensions/sample_project/sample/app/features/cli/__init__.py +0 -0
  40. cmdbox/extensions/sample_project/sample/app/features/web/__init__.py +0 -0
  41. cmdbox/extensions/sample_project/sample/extensions/features.yml +18 -0
  42. cmdbox/extensions/sample_project/sample/extensions/user_list.yml +2 -1
  43. cmdbox/extensions/sample_project/sample/logconf_sample.yml +14 -1
  44. cmdbox/extensions/user_list.yml +1 -0
  45. cmdbox/licenses/LICENSE.Authlib.1.5.2(BSD License).txt +29 -0
  46. cmdbox/licenses/LICENSE.Deprecated.1.2.18(MIT License).txt +21 -0
  47. cmdbox/licenses/LICENSE.SQLAlchemy.2.0.40(MIT License).txt +19 -0
  48. cmdbox/licenses/LICENSE.aiohappyeyeballs.2.6.1(Python Software Foundation License).txt +279 -0
  49. cmdbox/licenses/LICENSE.aiohttp.3.11.18(Apache Software License).txt +13 -0
  50. cmdbox/licenses/LICENSE.aiosignal.1.3.2(Apache Software License).txt +201 -0
  51. cmdbox/licenses/LICENSE.attrs.25.3.0(UNKNOWN).txt +21 -0
  52. cmdbox/licenses/LICENSE.cachetools.5.5.2(MIT License).txt +20 -0
  53. cmdbox/licenses/LICENSE.distro.1.9.0(Apache Software License).txt +202 -0
  54. cmdbox/licenses/LICENSE.docstring_parser.0.16(MIT License).txt +21 -0
  55. cmdbox/licenses/LICENSE.filelock.3.18.0(The Unlicense (Unlicense)).txt +24 -0
  56. cmdbox/licenses/LICENSE.frozenlist.1.6.0(Apache-2.0).txt +201 -0
  57. cmdbox/licenses/LICENSE.fsspec.2025.3.2(BSD License).txt +29 -0
  58. cmdbox/licenses/LICENSE.google-adk.0.5.0(Apache Software License).txt +202 -0
  59. cmdbox/licenses/LICENSE.google-api-python-client.2.169.0(Apache Software License).txt +201 -0
  60. cmdbox/licenses/LICENSE.google-auth-httplib2.0.2.0(Apache Software License).txt +201 -0
  61. cmdbox/licenses/LICENSE.google-auth.2.40.1(Apache Software License).txt +201 -0
  62. cmdbox/licenses/LICENSE.google-cloud-aiplatform.1.92.0(Apache 2.0).txt +202 -0
  63. cmdbox/licenses/LICENSE.google-cloud-bigquery.3.31.0(Apache Software License).txt +202 -0
  64. cmdbox/licenses/LICENSE.google-cloud-core.2.4.3(Apache Software License).txt +202 -0
  65. cmdbox/licenses/LICENSE.google-cloud-resource-manager.1.14.2(Apache Software License).txt +202 -0
  66. cmdbox/licenses/LICENSE.google-cloud-secret-manager.2.23.3(Apache Software License).txt +202 -0
  67. cmdbox/licenses/LICENSE.google-cloud-speech.2.32.0(Apache Software License).txt +202 -0
  68. cmdbox/licenses/LICENSE.google-cloud-storage.2.19.0(Apache Software License).txt +202 -0
  69. cmdbox/licenses/LICENSE.google-cloud-trace.1.16.1(Apache Software License).txt +202 -0
  70. cmdbox/licenses/LICENSE.google-crc32c.1.7.1(Apache 2.0).txt +202 -0
  71. cmdbox/licenses/LICENSE.google-genai.1.14.0(Apache Software License).txt +202 -0
  72. cmdbox/licenses/LICENSE.google-resumable-media.2.7.2(Apache Software License).txt +202 -0
  73. cmdbox/licenses/LICENSE.googleapis-common-protos.1.70.0(Apache Software License).txt +202 -0
  74. cmdbox/licenses/LICENSE.graphviz.0.20.3(MIT License).txt +21 -0
  75. cmdbox/licenses/LICENSE.grpc-google-iam-v1.0.14.2(Apache Software License).txt +202 -0
  76. cmdbox/licenses/LICENSE.grpcio-status.1.71.0(Apache Software License).txt +610 -0
  77. cmdbox/licenses/LICENSE.grpcio.1.71.0(Apache Software License).txt +610 -0
  78. cmdbox/licenses/LICENSE.httpcore.1.0.9(BSD License).txt +27 -0
  79. cmdbox/licenses/LICENSE.httplib2.0.22.0(MIT License).txt +23 -0
  80. cmdbox/licenses/LICENSE.httpx-sse.0.4.0(MIT).txt +21 -0
  81. cmdbox/licenses/LICENSE.httpx.0.28.1(BSD License).txt +12 -0
  82. cmdbox/licenses/LICENSE.huggingface-hub.0.31.1(Apache Software License).txt +201 -0
  83. cmdbox/licenses/LICENSE.importlib_metadata.8.6.1(Apache Software License).txt +202 -0
  84. cmdbox/licenses/LICENSE.jiter.0.9.0(MIT License).txt +1 -0
  85. cmdbox/licenses/LICENSE.jsonschema-specifications.2025.4.1(UNKNOWN).txt +19 -0
  86. cmdbox/licenses/LICENSE.jsonschema.4.23.0(MIT License).txt +19 -0
  87. cmdbox/licenses/LICENSE.litellm.1.69.0(MIT License).txt +26 -0
  88. cmdbox/licenses/LICENSE.mcp.1.8.0(MIT License).txt +21 -0
  89. cmdbox/licenses/LICENSE.multidict.6.4.3(Apache Software License).txt +13 -0
  90. cmdbox/licenses/LICENSE.openai.1.75.0(Apache Software License).txt +201 -0
  91. cmdbox/licenses/LICENSE.opentelemetry-api.1.33.0(Apache Software License).txt +201 -0
  92. cmdbox/licenses/LICENSE.opentelemetry-exporter-gcp-trace.1.9.0(Apache Software License).txt +201 -0
  93. cmdbox/licenses/LICENSE.opentelemetry-resourcedetector-gcp.1.9.0a0(Apache Software License).txt +201 -0
  94. cmdbox/licenses/LICENSE.opentelemetry-sdk.1.33.0(Apache Software License).txt +201 -0
  95. cmdbox/licenses/LICENSE.opentelemetry-semantic-conventions.0.54b0(Apache Software License).txt +201 -0
  96. cmdbox/licenses/LICENSE.propcache.0.3.1(Apache Software License).txt +202 -0
  97. cmdbox/licenses/LICENSE.proto-plus.1.26.1(Apache Software License).txt +202 -0
  98. cmdbox/licenses/LICENSE.protobuf.5.29.4(3-Clause BSD License).txt +32 -0
  99. cmdbox/licenses/LICENSE.pyasn1.0.6.1(BSD License).txt +24 -0
  100. cmdbox/licenses/LICENSE.pyasn1_modules.0.4.2(BSD License).txt +24 -0
  101. cmdbox/licenses/LICENSE.pydantic-settings.2.9.1(MIT License).txt +21 -0
  102. cmdbox/licenses/LICENSE.pyparsing.3.2.3(MIT License).txt +18 -0
  103. cmdbox/licenses/LICENSE.python-dateutil.2.9.0.post0(Apache Software License; BSD License).txt +54 -0
  104. cmdbox/licenses/LICENSE.referencing.0.36.2(UNKNOWN).txt +19 -0
  105. cmdbox/licenses/LICENSE.regex.2024.11.6(Apache Software License).txt +208 -0
  106. cmdbox/licenses/LICENSE.rpds-py.0.24.0(MIT).txt +19 -0
  107. cmdbox/licenses/LICENSE.rsa.4.9.1(Apache Software License).txt +13 -0
  108. cmdbox/licenses/LICENSE.shapely.2.1.0(BSD License).txt +29 -0
  109. cmdbox/licenses/LICENSE.sse-starlette.2.3.4(BSD License).txt +27 -0
  110. cmdbox/licenses/LICENSE.tiktoken.0.9.0(MIT License).txt +21 -0
  111. cmdbox/licenses/LICENSE.tokenizers.0.21.1(Apache Software License).txt +1 -0
  112. cmdbox/licenses/LICENSE.tqdm.4.67.1(MIT License; Mozilla Public License 2.0 (MPL 2.0)).txt +49 -0
  113. cmdbox/licenses/LICENSE.tzlocal.5.3.1(MIT License).txt +19 -0
  114. cmdbox/licenses/LICENSE.uritemplate.4.1.1(Apache Software License; BSD License).txt +3 -0
  115. cmdbox/licenses/LICENSE.wrapt.1.17.2(BSD License).txt +24 -0
  116. cmdbox/licenses/LICENSE.yarl.1.20.0(Apache Software License).txt +202 -0
  117. cmdbox/licenses/files.txt +104 -11
  118. cmdbox/logconf_audit.yml +16 -3
  119. cmdbox/logconf_client.yml +16 -3
  120. cmdbox/logconf_cmdbox.yml +16 -3
  121. cmdbox/logconf_edge.yml +16 -3
  122. cmdbox/logconf_gui.yml +15 -2
  123. cmdbox/logconf_server.yml +15 -2
  124. cmdbox/logconf_web.yml +15 -2
  125. cmdbox/version.py +3 -2
  126. cmdbox/web/agent.html +263 -0
  127. cmdbox/web/assets/cmdbox/agent.js +338 -0
  128. cmdbox/web/assets/cmdbox/common.js +1111 -1020
  129. cmdbox/web/assets/cmdbox/main.js +17 -3
  130. cmdbox/web/assets/cmdbox/signin.js +4 -4
  131. cmdbox/web/assets/filer/filer.js +4 -2
  132. {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/METADATA +69 -26
  133. {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/RECORD +148 -67
  134. /cmdbox/licenses/{LICENSE.charset-normalizer.3.4.1(MIT License).txt → LICENSE.charset-normalizer.3.4.2(MIT License).txt} +0 -0
  135. /cmdbox/licenses/{LICENSE.click.8.1.8(BSD License).txt → LICENSE.click.8.2.0(UNKNOWN).txt} +0 -0
  136. /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
  137. /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
  138. /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
  139. /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
  140. /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
  141. /cmdbox/licenses/{LICENSE.pydantic.2.11.3(MIT License).txt → LICENSE.pydantic.2.11.4(MIT License).txt} +0 -0
  142. /cmdbox/licenses/{LICENSE.pydantic_core.2.33.1(MIT License).txt → LICENSE.pydantic_core.2.33.2(MIT License).txt} +0 -0
  143. /cmdbox/licenses/{LICENSE.redis.5.2.1(MIT License).txt → LICENSE.redis.6.0.0(MIT License).txt} +0 -0
  144. /cmdbox/licenses/{LICENSE.snowballstemmer.2.2.0(BSD License).txt → LICENSE.snowballstemmer.3.0.1(BSD License).txt} +0 -0
  145. {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/LICENSE +0 -0
  146. {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/WHEEL +0 -0
  147. {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/entry_points.txt +0 -0
  148. {cmdbox-0.5.4.dist-info → cmdbox-0.6.0.1.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,15 @@
1
1
  from cmdbox.app import common, feature, web
2
2
  from cmdbox.app.options import Options
3
+ from cmdbox.app.features.cli import agent_base
3
4
  from pathlib import Path
4
5
  from typing import Dict, Any, Tuple, List, Union
6
+ from urllib.request import pathname2url
5
7
  import argparse
6
8
  import logging
7
9
  import multiprocessing
8
10
 
9
11
 
10
- class WebStart(feature.UnsupportEdgeFeature):
12
+ class WebStart(feature.UnsupportEdgeFeature, agent_base.AgentBase):
11
13
  def get_mode(self) -> Union[str, List[str]]:
12
14
  """
13
15
  この機能のモードを返します
@@ -25,7 +27,7 @@ class WebStart(feature.UnsupportEdgeFeature):
25
27
  str: コマンド
26
28
  """
27
29
  return 'start'
28
-
30
+
29
31
  def get_option(self):
30
32
  """
31
33
  この機能のオプションを返します
@@ -33,106 +35,107 @@ class WebStart(feature.UnsupportEdgeFeature):
33
35
  Returns:
34
36
  Dict[str, Any]: オプション
35
37
  """
36
- return dict(
37
- use_redis=self.USE_REDIS_MEIGHT, nouse_webmode=True,
38
- discription_ja="Webモードを起動します。",
39
- discription_en="Start Web mode.",
40
- choice=[
41
- dict(opt="host", type=Options.T_STR, default=self.default_host, required=True, multi=False, hide=True, choice=None, web="mask",
42
- discription_ja="Redisサーバーのサービスホストを指定します。",
43
- discription_en="Specify the service host of the Redis server."),
44
- dict(opt="port", type=Options.T_INT, default=self.default_port, required=True, multi=False, hide=True, choice=None, web="mask",
45
- discription_ja="Redisサーバーのサービスポートを指定します。",
46
- discription_en="Specify the service port of the Redis server."),
47
- dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
48
- discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
49
- discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
50
- dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
51
- discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
52
- discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
53
- dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
54
- discription_ja=f"省略した時は `$HONE/.{self.ver.__appid__}` を使用します。",
55
- discription_en=f"When omitted, `$HONE/.{self.ver.__appid__}` is used."),
56
- dict(opt="allow_host", type=Options.T_STR, default="0.0.0.0", required=False, multi=False, hide=False, choice=None,
57
- discription_ja="省略した時は `0.0.0.0` を使用します。",
58
- discription_en="If omitted, `0.0.0.0` is used."),
59
- dict(opt="listen_port", type=Options.T_INT, default="8081", required=False, multi=False, hide=False, choice=None,
60
- discription_ja="省略した時は `8081` を使用します。",
61
- discription_en="If omitted, `8081` is used."),
62
- dict(opt="ssl_listen_port", type=Options.T_INT, default="8443", required=False, multi=False, hide=False, choice=None,
63
- discription_ja="省略した時は `8443` を使用します。",
64
- discription_en="If omitted, `8443` is used."),
65
- dict(opt="ssl_cert", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
66
- discription_ja="SSLサーバー証明書ファイルを指定します。",
67
- discription_en="Specify the SSL server certificate file."),
68
- dict(opt="ssl_key", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
69
- discription_ja="SSLサーバー秘密鍵ファイルを指定します。",
70
- discription_en="Specify the SSL server private key file."),
71
- dict(opt="ssl_keypass", type=Options.T_STR, default=None, required=False, multi=False, hide=True, choice=None,
72
- discription_ja="SSLサーバー秘密鍵ファイルの複合化パスワードを指定します。",
73
- discription_en="Specify the composite password for the SSL server private key file."),
74
- dict(opt="ssl_ca_certs", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
75
- discription_ja="SSLサーバーCA証明書ファイルを指定します。",
76
- discription_en="Specify the SSL server CA certificate file."),
77
- dict(opt="signin_file", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
78
- discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
79
- discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
80
- dict(opt="session_domain", type=Options.T_STR, default=None, required=False, multi=False, hide=True, choice=None,
81
- discription_ja="サインインしたユーザーのセッションが有効なドメインを指定します。",
82
- discription_en="Specify the domain for which the signed-in user's session is valid."),
83
- dict(opt="session_path", type=Options.T_STR, default="/", required=False, multi=False, hide=True, choice=None,
84
- discription_ja="サインインしたユーザーのセッションが有効なパスを指定します。",
85
- discription_en="Specify the session timeout in seconds for signed-in users."),
86
- dict(opt="session_secure", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
87
- discription_ja="サインインしたユーザーのセッションにSecureフラグを設定します。",
88
- discription_en="Set the Secure flag for the signed-in user's session."),
89
- dict(opt="session_timeout", type=Options.T_INT, default="900", required=False, multi=False, hide=True, choice=None,
90
- discription_ja="サインインしたユーザーのセッションタイムアウトの時間を秒で指定します。",
91
- discription_en="Specify the session timeout in seconds for signed-in users."),
92
- dict(opt="guvicorn_workers", type=Options.T_INT, default=multiprocessing.cpu_count()*2, required=False, multi=False, hide=True, choice=None,
93
- discription_ja="guvicornワーカー数を指定します。Linux環境でのみ有効です。-1又は未指定の場合はCPU数の2倍を使用します。",
94
- discription_en="Specifies the number of guvicorn workers, valid only in Linux environment. If -1 or unspecified, twice the number of CPUs is used."),
95
- dict(opt="guvicorn_timeout", type=Options.T_INT, default=30, required=False, multi=False, hide=True, choice=None,
96
- discription_ja="guvicornワーカーのタイムアウトの時間を秒で指定します。",
97
- discription_en="Specify the timeout duration of the guvicorn worker in seconds."),
98
- dict(opt="client_only", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
99
- discription_ja="サーバーへの接続を行わないようにします。",
100
- discription_en="Do not make connections to the server."),
101
- dict(opt="outputs_key", type=Options.T_STR, default=None, required=False, multi=True, hide=False, choice=None,
102
- discription_ja="showimg及びwebcap画面で表示する項目を指定します。省略した場合は全ての項目を表示します。",
103
- discription_en="Specify items to be displayed on the showimg and webcap screens. If omitted, all items are displayed."),
104
- dict(opt="doc_root", type=Options.T_DIR, default=None, required=False, multi=False, hide=False, choice=None,
105
- discription_ja="カスタムファイルのドキュメントルート. フォルダ指定のカスタムファイルのパスから、doc_rootのパスを除去したパスでURLマッピングします。",
106
- discription_en="Document root for custom files. URL mapping from the path of a folder-specified custom file with the path of doc_root removed."),
107
- dict(opt="gui_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
108
- discription_ja="`gui.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
109
- discription_en="Specify `gui.html`. If omitted, the cmdbox built-in HTML file is used."),
110
- dict(opt="filer_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
111
- discription_ja="`filer.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
112
- discription_en="Specify `filer.html`. If omitted, the cmdbox built-in HTML file is used."),
113
- dict(opt="result_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
114
- discription_ja="`result.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
115
- discription_en="Specify `result.html`. If omitted, the cmdbox built-in HTML file is used."),
116
- dict(opt="users_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
117
- discription_ja="`users.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
118
- discription_en="Specify `users.html`. If omitted, the cmdbox built-in HTML file is used."),
119
- dict(opt="assets", type=Options.T_FILE, default=None, required=False, multi=True, hide=False, choice=None,
120
- discription_ja="htmlファイルを使用する場合に必要なアセットファイルを指定します。",
121
- discription_en="Specify the asset file required when using html files."),
122
- dict(opt="signin_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
123
- discription_ja="`signin.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
124
- discription_en="Specify `signin.html`. If omitted, the cmdbox built-in HTML file is used."),
125
- dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
126
- discription_ja="GUIモードでのみ使用可能です。コマンド実行時の標準出力をConsole logに出力します。",
127
- discription_en="Available only in GUI mode. Outputs standard output during command execution to Console log."),
128
- dict(opt="capture_stdout", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
129
- discription_ja="GUIモードでのみ使用可能です。コマンド実行時の標準出力をキャプチャーし、実行結果画面に表示します。",
130
- discription_en="Available only in GUI mode. Captures standard output during command execution and displays it on the execution result screen."),
131
- dict(opt="capture_maxsize", type=Options.T_INT, default=self.DEFAULT_CAPTURE_MAXSIZE, required=False, multi=False, hide=True, choice=None,
132
- discription_ja="GUIモードでのみ使用可能です。コマンド実行時の標準出力の最大キャプチャーサイズを指定します。",
133
- discription_en="Available only in GUI mode. Specifies the maximum capture size of standard output when executing commands."),
38
+ opt = super().get_option()
39
+ opt['use_redis'] = self.USE_REDIS_MEIGHT
40
+ opt['nouse_webmode'] = True
41
+ opt['discription_ja'] = "Webモードを起動します。"
42
+ opt['discription_en'] = "Start Web mode."
43
+ opt['choice'] += [
44
+ dict(opt="host", type=Options.T_STR, default=self.default_host, required=True, multi=False, hide=True, choice=None, web="mask",
45
+ discription_ja="Redisサーバーのサービスホストを指定します。",
46
+ discription_en="Specify the service host of the Redis server."),
47
+ dict(opt="port", type=Options.T_INT, default=self.default_port, required=True, multi=False, hide=True, choice=None, web="mask",
48
+ discription_ja="Redisサーバーのサービスポートを指定します。",
49
+ discription_en="Specify the service port of the Redis server."),
50
+ dict(opt="password", type=Options.T_STR, default=self.default_pass, required=True, multi=False, hide=True, choice=None, web="mask",
51
+ discription_ja="Redisサーバーのアクセスパスワード(任意)を指定します。省略時は `password` を使用します。",
52
+ discription_en="Specify the access password of the Redis server (optional). If omitted, `password` is used."),
53
+ dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
54
+ discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
55
+ discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
56
+ dict(opt="data", type=Options.T_FILE, default=self.default_data, required=False, multi=False, hide=False, choice=None,
57
+ discription_ja=f"省略した時は `$HONE/.{self.ver.__appid__}` を使用します。",
58
+ discription_en=f"When omitted, `$HONE/.{self.ver.__appid__}` is used."),
59
+ dict(opt="allow_host", type=Options.T_STR, default="0.0.0.0", required=False, multi=False, hide=False, choice=None,
60
+ discription_ja="省略した時は `0.0.0.0` を使用します。",
61
+ discription_en="If omitted, `0.0.0.0` is used."),
62
+ dict(opt="listen_port", type=Options.T_INT, default="8081", required=False, multi=False, hide=False, choice=None,
63
+ discription_ja="省略した時は `8081` を使用します。",
64
+ discription_en="If omitted, `8081` is used."),
65
+ dict(opt="ssl_listen_port", type=Options.T_INT, default="8443", required=False, multi=False, hide=False, choice=None,
66
+ discription_ja="省略した時は `8443` を使用します。",
67
+ discription_en="If omitted, `8443` is used."),
68
+ dict(opt="ssl_cert", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
69
+ discription_ja="SSLサーバー証明書ファイルを指定します。",
70
+ discription_en="Specify the SSL server certificate file."),
71
+ dict(opt="ssl_key", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
72
+ discription_ja="SSLサーバー秘密鍵ファイルを指定します。",
73
+ discription_en="Specify the SSL server private key file."),
74
+ dict(opt="ssl_keypass", type=Options.T_STR, default=None, required=False, multi=False, hide=True, choice=None,
75
+ discription_ja="SSLサーバー秘密鍵ファイルの複合化パスワードを指定します。",
76
+ discription_en="Specify the composite password for the SSL server private key file."),
77
+ dict(opt="ssl_ca_certs", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
78
+ discription_ja="SSLサーバーCA証明書ファイルを指定します。",
79
+ discription_en="Specify the SSL server CA certificate file."),
80
+ dict(opt="signin_file", type=Options.T_FILE, default=None, required=False, multi=False, hide=True, choice=None,
81
+ discription_ja="サインイン可能なユーザーとパスワードを記載したファイルを指定します。省略した時は認証を要求しません。",
82
+ discription_en="Specify a file containing users and passwords with which they can signin. If omitted, no authentication is required."),
83
+ dict(opt="session_domain", type=Options.T_STR, default=None, required=False, multi=False, hide=True, choice=None,
84
+ discription_ja="サインインしたユーザーのセッションが有効なドメインを指定します。",
85
+ discription_en="Specify the domain for which the signed-in user's session is valid."),
86
+ dict(opt="session_path", type=Options.T_STR, default="/", required=False, multi=False, hide=True, choice=None,
87
+ discription_ja="サインインしたユーザーのセッションが有効なパスを指定します。",
88
+ discription_en="Specify the session timeout in seconds for signed-in users."),
89
+ dict(opt="session_secure", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
90
+ discription_ja="サインインしたユーザーのセッションにSecureフラグを設定します。",
91
+ discription_en="Set the Secure flag for the signed-in user's session."),
92
+ dict(opt="session_timeout", type=Options.T_INT, default="900", required=False, multi=False, hide=True, choice=None,
93
+ discription_ja="サインインしたユーザーのセッションタイムアウトの時間を秒で指定します。",
94
+ discription_en="Specify the session timeout in seconds for signed-in users."),
95
+ dict(opt="guvicorn_workers", type=Options.T_INT, default=multiprocessing.cpu_count()*2, required=False, multi=False, hide=True, choice=None,
96
+ discription_ja="guvicornワーカー数を指定します。Linux環境でのみ有効です。-1又は未指定の場合はCPU数の2倍を使用します。",
97
+ discription_en="Specifies the number of guvicorn workers, valid only in Linux environment. If -1 or unspecified, twice the number of CPUs is used."),
98
+ dict(opt="guvicorn_timeout", type=Options.T_INT, default=30, required=False, multi=False, hide=True, choice=None,
99
+ discription_ja="guvicornワーカーのタイムアウトの時間を秒で指定します。",
100
+ discription_en="Specify the timeout duration of the guvicorn worker in seconds."),
101
+ dict(opt="client_only", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
102
+ discription_ja="サーバーへの接続を行わないようにします。",
103
+ discription_en="Do not make connections to the server."),
104
+ dict(opt="outputs_key", type=Options.T_STR, default=None, required=False, multi=True, hide=False, choice=None,
105
+ discription_ja="showimg及びwebcap画面で表示する項目を指定します。省略した場合は全ての項目を表示します。",
106
+ discription_en="Specify items to be displayed on the showimg and webcap screens. If omitted, all items are displayed."),
107
+ dict(opt="doc_root", type=Options.T_DIR, default=None, required=False, multi=False, hide=False, choice=None,
108
+ discription_ja="カスタムファイルのドキュメントルート. フォルダ指定のカスタムファイルのパスから、doc_rootのパスを除去したパスでURLマッピングします。",
109
+ discription_en="Document root for custom files. URL mapping from the path of a folder-specified custom file with the path of doc_root removed."),
110
+ dict(opt="gui_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
111
+ discription_ja="`gui.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
112
+ discription_en="Specify `gui.html`. If omitted, the cmdbox built-in HTML file is used."),
113
+ dict(opt="filer_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
114
+ discription_ja="`filer.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
115
+ discription_en="Specify `filer.html`. If omitted, the cmdbox built-in HTML file is used."),
116
+ dict(opt="result_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
117
+ discription_ja="`result.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
118
+ discription_en="Specify `result.html`. If omitted, the cmdbox built-in HTML file is used."),
119
+ dict(opt="users_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
120
+ discription_ja="`users.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
121
+ discription_en="Specify `users.html`. If omitted, the cmdbox built-in HTML file is used."),
122
+ dict(opt="assets", type=Options.T_FILE, default=None, required=False, multi=True, hide=False, choice=None,
123
+ discription_ja="htmlファイルを使用する場合に必要なアセットファイルを指定します。",
124
+ discription_en="Specify the asset file required when using html files."),
125
+ dict(opt="signin_html", type=Options.T_FILE, default=None, required=False, multi=False, hide=False, choice=None,
126
+ discription_ja="`signin.html` を指定します。省略時はcmdbox内蔵のHTMLファイルを使用します。",
127
+ discription_en="Specify `signin.html`. If omitted, the cmdbox built-in HTML file is used."),
128
+ dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
129
+ discription_ja="GUIモードでのみ使用可能です。コマンド実行時の標準出力をConsole logに出力します。",
130
+ discription_en="Available only in GUI mode. Outputs standard output during command execution to Console log."),
131
+ dict(opt="capture_stdout", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
132
+ discription_ja="GUIモードでのみ使用可能です。コマンド実行時の標準出力をキャプチャーし、実行結果画面に表示します。",
133
+ discription_en="Available only in GUI mode. Captures standard output during command execution and displays it on the execution result screen."),
134
+ dict(opt="capture_maxsize", type=Options.T_INT, default=self.DEFAULT_CAPTURE_MAXSIZE, required=False, multi=False, hide=True, choice=None,
135
+ discription_ja="GUIモードでのみ使用可能です。コマンド実行時の標準出力の最大キャプチャーサイズを指定します。",
136
+ discription_en="Available only in GUI mode. Specifies the maximum capture size of standard output when executing commands."),
134
137
  ]
135
- )
138
+ return opt
136
139
 
137
140
  def apprun(self, logger:logging.Logger, args:argparse.Namespace, tm:float, pf:List[Dict[str, float]]=[]) -> Tuple[int, Dict[str, Any], Any]:
138
141
  """
@@ -153,6 +156,7 @@ class WebStart(feature.UnsupportEdgeFeature):
153
156
  return 1, msg, None
154
157
  w = None
155
158
  try:
159
+ args.gui_mode = False if not hasattr(args, 'gui_mode') or not args.gui_mode else args.gui_mode
156
160
  ssl_cert = None if args.ssl_cert is None else Path(args.ssl_cert)
157
161
  ssl_key = None if args.ssl_key is None else Path(args.ssl_key)
158
162
  ssl_ca_certs = None if args.ssl_ca_certs is None else Path(args.ssl_ca_certs)
@@ -160,12 +164,24 @@ class WebStart(feature.UnsupportEdgeFeature):
160
164
  redis_host=args.host, redis_port=args.port, redis_password=args.password, svname=args.svname,
161
165
  client_only=args.client_only, doc_root=args.doc_root, gui_html=args.gui_html, filer_html=args.filer_html,
162
166
  result_html=args.result_html, users_html=args.users_html,
163
- assets=args.assets, signin_html=args.signin_html, signin_file=args.signin_file)
164
- w.start(args.allow_host, args.listen_port, ssl_listen_port=args.ssl_listen_port,
167
+ assets=args.assets, signin_html=args.signin_html, signin_file=args.signin_file, gui_mode=args.gui_mode)
168
+ agent_runner = None
169
+ mcp = None
170
+ logger.info(f"Agent={args.agent}")
171
+ if args.agent=='use':
172
+ if args.agent_session_store == 'sqlite':
173
+ args.agent_session_dburl = "sqlite:" + pathname2url(str(w.agent_path / 'session.db'))
174
+ elif args.agent_session_store == 'postgresql':
175
+ args.agent_session_dburl = f"postgresql+psycopg://{args.agent_pg_user}:{args.agent_pg_password}@{args.agent_pg_host}:{args.agent_pg_port}/{args.agent_pg_dbname}"
176
+ else:
177
+ args.agent_session_dburl = None
178
+ agent_runner, mcp = self.init_agent_runner(logger, args)
179
+ w.start(allow_host=args.allow_host, listen_port=args.listen_port, ssl_listen_port=args.ssl_listen_port,
165
180
  ssl_cert=ssl_cert, ssl_key=ssl_key, ssl_keypass=args.ssl_keypass, ssl_ca_certs=ssl_ca_certs,
166
181
  session_domain=args.session_domain, session_path=args.session_path,
167
182
  session_secure=args.session_secure, session_timeout=args.session_timeout,
168
- outputs_key=args.outputs_key, guvicorn_workers=args.guvicorn_workers, guvicorn_timeout=args.guvicorn_timeout)
183
+ outputs_key=args.outputs_key, guvicorn_workers=args.guvicorn_workers, guvicorn_timeout=args.guvicorn_timeout,
184
+ agent_runner=agent_runner, mcp=mcp, mcp_listen_port=args.mcp_listen_port, mcp_ssl_listen_port=args.mcp_ssl_listen_port)
169
185
 
170
186
  msg = dict(success="web complate.")
171
187
  common.print_format(msg, args.format, tm, args.output_json, args.output_json_append, pf=pf)
@@ -37,7 +37,7 @@ class WebStop(feature.UnsupportEdgeFeature):
37
37
  discription_ja="Webモードを停止します。",
38
38
  discription_en="Stop Web mode.",
39
39
  choice=[
40
- dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f'.{self.ver.__appid__}', required=False, multi=False, hide=False, choice=None,
40
+ dict(opt="data", type=Options.T_FILE, default=self.default_data, required=False, multi=False, hide=False, choice=None,
41
41
  discription_ja=f"省略した時は `$HONE/.{self.ver.__appid__}` を使用します。",
42
42
  discription_en=f"When omitted, `$HONE/.{self.ver.__appid__}` is used."),
43
43
  dict(opt="stdout_log", type=Options.T_BOOL, default=True, required=False, multi=False, hide=True, choice=[True, False],
@@ -49,7 +49,7 @@ class WebUserAdd(feature.UnsupportEdgeFeature):
49
49
  dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
50
50
  discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
51
51
  discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
52
- dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
52
+ dict(opt="data", type=Options.T_FILE, default=self.default_data, required=False, multi=False, hide=False, choice=None,
53
53
  discription_ja=f"省略した時は `$HONE/.{self.ver.__appid__}` を使用します。",
54
54
  discription_en=f"When omitted, `$HONE/.{self.ver.__appid__}` is used."),
55
55
  dict(opt="user_id", type=Options.T_INT, default=None, required=True, multi=False, hide=False, choice=None,
@@ -49,7 +49,7 @@ class WebUserDel(feature.UnsupportEdgeFeature):
49
49
  dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
50
50
  discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
51
51
  discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
52
- dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
52
+ dict(opt="data", type=Options.T_FILE, default=self.default_data, required=False, multi=False, hide=False, choice=None,
53
53
  discription_ja=f"省略した時は `$HONE/.{self.ver.__appid__}` を使用します。",
54
54
  discription_en=f"When omitted, `$HONE/.{self.ver.__appid__}` is used."),
55
55
  dict(opt="user_id", type=Options.T_INT, default=None, required=True, multi=False, hide=False, choice=None,
@@ -49,7 +49,7 @@ class WebUserEdit(feature.UnsupportEdgeFeature):
49
49
  dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
50
50
  discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
51
51
  discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
52
- dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
52
+ dict(opt="data", type=Options.T_FILE, default=self.default_data, required=False, multi=False, hide=False, choice=None,
53
53
  discription_ja=f"省略した時は `$HONE/.{self.ver.__appid__}` を使用します。",
54
54
  discription_en=f"When omitted, `$HONE/.{self.ver.__appid__}` is used."),
55
55
  dict(opt="user_id", type=Options.T_INT, default=None, required=True, multi=False, hide=False, choice=None,
@@ -49,7 +49,7 @@ class WebUserList(feature.OneshotResultEdgeFeature):
49
49
  dict(opt="svname", type=Options.T_STR, default=self.default_svname, required=True, multi=False, hide=True, choice=None, web="readonly",
50
50
  discription_ja="サーバーのサービス名を指定します。省略時は `server` を使用します。",
51
51
  discription_en="Specify the service name of the inference server. If omitted, `server` is used."),
52
- dict(opt="data", type=Options.T_FILE, default=common.HOME_DIR / f".{self.ver.__appid__}", required=False, multi=False, hide=False, choice=None,
52
+ dict(opt="data", type=Options.T_FILE, default=self.default_data, required=False, multi=False, hide=False, choice=None,
53
53
  discription_ja=f"省略した時は `$HONE/.{self.ver.__appid__}` を使用します。",
54
54
  discription_en=f"When omitted, `$HONE/.{self.ver.__appid__}` is used."),
55
55
  dict(opt="user_name", type=Options.T_STR, default=None, required=False, multi=False, hide=False, choice=None,
@@ -0,0 +1,262 @@
1
+ from cmdbox.app import common, feature
2
+ from cmdbox.app.auth import signin
3
+ from cmdbox.app.commons import convert
4
+ from cmdbox.app.web import Web
5
+ from fastapi import FastAPI, Depends, HTTPException, Request, Response, WebSocket
6
+ from fastapi.responses import HTMLResponse, StreamingResponse
7
+ from starlette.websockets import WebSocketDisconnect
8
+ from pathlib import Path
9
+ from starlette.applications import Starlette
10
+ from starlette.datastructures import UploadFile
11
+ from starlette.routing import Mount
12
+ from typing import Dict, Any, Tuple, List, Union
13
+ import locale
14
+ import logging
15
+ import json
16
+ import re
17
+ import time
18
+ import traceback
19
+
20
+ class Agent(feature.WebFeature):
21
+ def route(self, web:Web, app:FastAPI) -> None:
22
+ """
23
+ webモードのルーティングを設定します
24
+
25
+ Args:
26
+ web (Web): Webオブジェクト
27
+ app (FastAPI): FastAPIオブジェクト
28
+ """
29
+ if web.agent_html is not None:
30
+ if not web.agent_html.is_file():
31
+ raise FileNotFoundError(f'agent_html is not found. ({web.agent_html})')
32
+ with open(web.agent_html, 'r', encoding='utf-8') as f:
33
+ web.agent_html_data = f.read()
34
+
35
+ @app.get('/agent', response_class=HTMLResponse)
36
+ @app.post('/agent', response_class=HTMLResponse)
37
+ async def agent(req:Request, res:Response):
38
+ signin = web.signin.check_signin(req, res)
39
+ if signin is not None:
40
+ return signin
41
+ res.headers['Access-Control-Allow-Origin'] = '*'
42
+ web.options.audit_exec(req, res, web)
43
+ return web.agent_html_data
44
+
45
+ @app.post('/agent/session/list')
46
+ async def agent_session_list(req:Request, res:Response):
47
+ signin = web.signin.check_signin(req, res)
48
+ if signin is not None:
49
+ return signin
50
+ if web.agent_runner is None:
51
+ web.logger.error(f"agent_runner is null. Start web mode with `--agent use`.")
52
+ raise HTTPException(status_code=500, detail='agent_runner is null. Start web mode with `--agent use`.')
53
+ res.headers['Access-Control-Allow-Origin'] = '*'
54
+ web.options.audit_exec(req, res, web)
55
+ # ユーザー名を取得する
56
+ user_id = common.random_string(16)
57
+ if 'signin' in req.session:
58
+ user_id = req.session['signin']['name']
59
+ form = await req.form()
60
+ session_id = form.get('session_id', None)
61
+ sessions = await web.list_agent_sessions(web.agent_runner.session_service, user_id, session_id=session_id)
62
+ data = [dict(id=s.id, app_name=s.app_name, user_id=s.user_id, last_update_time=s.last_update_time,
63
+ events=[dict(author=ev.author,text=ev.content.parts[0].text) for ev in s.events if ev.content and ev.content.parts]) for s in sessions]
64
+ data.reverse() # 最新のセッションを先頭にする
65
+ return dict(success=data)
66
+
67
+ @app.post('/agent/session/delete')
68
+ async def agent_session_delete(req:Request, res:Response):
69
+ signin = web.signin.check_signin(req, res)
70
+ if signin is not None:
71
+ return signin
72
+ if web.agent_runner is None:
73
+ web.logger.error(f"agent_runner is null. Start web mode with `--agent use`.")
74
+ raise HTTPException(status_code=500, detail='agent_runner is null. Start web mode with `--agent use`.')
75
+ res.headers['Access-Control-Allow-Origin'] = '*'
76
+ web.options.audit_exec(req, res, web)
77
+ # ユーザー名を取得する
78
+ user_id = common.random_string(16)
79
+ if 'signin' in req.session:
80
+ user_id = req.session['signin']['name']
81
+ form = await req.form()
82
+ session_id = form.get('session_id', None)
83
+ await web.delete_agent_session(web.agent_runner.session_service, user_id, session_id=session_id)
84
+ return dict(success=True)
85
+
86
+ @app.websocket('/agent/chat/ws')
87
+ @app.websocket('/agent/chat/ws/{session_id}')
88
+ async def ws_chat(session_id:str=None, websocket:WebSocket=None, res:Response=None, scope=Depends(signin.create_request_scope)):
89
+ if not websocket:
90
+ raise HTTPException(status_code=400, detail='Expected WebSocket request.')
91
+ signin = web.signin.check_signin(websocket, res)
92
+ if signin is not None:
93
+ return signin
94
+ if web.agent_runner is None:
95
+ web.logger.error(f"agent_runner is null. Start web mode with `--agent use`.")
96
+ raise HTTPException(status_code=500, detail='agent_runner is null. Start web mode with `--agent use`.')
97
+
98
+ # これを行わねば非同期処理にならない。。
99
+ await websocket.accept()
100
+ # チャット処理
101
+ async for res in _chat(websocket.session, session_id, websocket, websocket.receive_text):
102
+ await websocket.send_text(res)
103
+ return dict(success="connected")
104
+
105
+ @app.api_route('/agent/chat/stream', methods=['GET', 'POST'])
106
+ @app.api_route('/agent/chat/stream/{session_id}', methods=['GET', 'POST'])
107
+ async def sse_chat(session_id:str=None, req:Request=None, res:Response=None):
108
+ signin = web.signin.check_signin(req, res)
109
+ if signin is not None:
110
+ return signin
111
+ def _marge_opt(opt, param):
112
+ for k in opt.keys():
113
+ if k in param: opt[k] = param[k]
114
+ return opt
115
+ content_type = req.headers.get('content-type')
116
+ opt = None
117
+ if content_type is None:
118
+ opt = req.query_params._dict
119
+ elif content_type.startswith('multipart/form-data'):
120
+ form = await req.form()
121
+ opt = dict()
122
+ for key, fv in form.multi_items():
123
+ if not isinstance(fv, UploadFile): continue
124
+ opt[key] = fv.file
125
+ elif content_type.startswith('application/json'):
126
+ opt = await req.json()
127
+ elif content_type.startswith('application/octet-stream'):
128
+ opt = json.loads(await req.body())
129
+ if opt is None:
130
+ raise HTTPException(status_code=400, detail='Expected JSON or form data.')
131
+ if opt['query'] is None or opt['query'] == '':
132
+ raise HTTPException(status_code=400, detail='Expected query.')
133
+ if web.agent_runner is None:
134
+ web.logger.error(f"agent_runner is null. Start web mode with `--agent use`.")
135
+ raise HTTPException(status_code=500, detail='agent_runner is null. Start web mode with `--agent use`.')
136
+ async def receive_text():
137
+ # 受信したデータを返す
138
+ if 'query' in opt:
139
+ query = opt['query']
140
+ del opt['query']
141
+ return query
142
+ raise self.SSEDisconnect('SSE disconnect')
143
+ # チャット処理
144
+ return StreamingResponse(
145
+ _chat(req.session, session_id, req, receive_text=receive_text)
146
+ )
147
+
148
+ async def _chat(session:Dict[str, Any], session_id:str, sock, receive_text=None):
149
+ if web.logger.level == logging.DEBUG:
150
+ web.logger.debug(f"agent_chat: connected")
151
+ # ユーザー名を取得する
152
+ user_id = common.random_string(16)
153
+ groups = []
154
+ if 'signin' in session:
155
+ user_id = session['signin']['name']
156
+ groups = session['signin']['groups']
157
+ # 言語認識
158
+ language, _ = locale.getlocale()
159
+ is_japan = language.find('Japan') >= 0 or language.find('ja_JP') >= 0
160
+ # セッションを作成する
161
+ agent_session = await web.create_agent_session(web.agent_runner.session_service, user_id, session_id=session_id)
162
+ startmsg = "こんにちは!何かお手伝いできることはありますか?" if is_japan else "Hello! Is there anything I can help you with?"
163
+ yield json.dumps(dict(message=startmsg), default=common.default_json_enc)
164
+ def _replace_match(match_obj):
165
+ json_str = match_obj.group(0)
166
+ try:
167
+ data = json.loads(json_str) # ユニコード文字列をエンコード
168
+ return json.dumps(data, ensure_ascii=False, default=common.default_json_enc)
169
+ except json.JSONDecodeError:
170
+ return json_str
171
+ json_pattern = re.compile(r'\{.*?\}')
172
+
173
+ from google.genai import types
174
+ while True:
175
+ outputs = None
176
+ try:
177
+ query = await receive_text()
178
+ if query is None or query == '' or query == 'ping':
179
+ time.sleep(0.5)
180
+ continue
181
+ """
182
+ if is_japan:
183
+ query += f"<important>なお現在のユーザーは'{user_id}'でgroupsは'{groups}'ですので引数に必要な時は指定してください。" + \
184
+ f"またsignin_fileの引数が必要な時は'{web.signin.signin_file}'を指定してください。</important>"
185
+ #f"またコマンド実行に必要なパラメータを確認し、以下の引数が必要な場合はこの値を使用してください。\n" + \
186
+ #f" host = {web.redis_host if web.redis_host else self.default_host}\n" + \
187
+ #f", port = {web.redis_port if web.redis_port else self.default_port}\n" + \
188
+ #f", password = {web.redis_password if web.redis_password else self.default_pass}\n" + \
189
+ #f", svname = {web.svname if web.svname else self.default_svname}\n"
190
+ else:
191
+ query += f"<important>The current user is '{user_id}' and the groups is '{groups}', so please specify it when necessary." + \
192
+ f"Also, if the signin_file argument is required for command execution, please specify '{web.signin.signin_file}'.</important>"
193
+ #f"Also check the parameters required to execute the command and use these values if the following arguments are required.\n" + \
194
+ #f" host = {web.redis_host if web.redis_host else self.default_host}\n" + \
195
+ #f", port = {web.redis_port if web.redis_port else self.default_port}\n" + \
196
+ #f", password = {web.redis_password if web.redis_password else self.default_pass}\n" + \
197
+ #f", svname = {web.svname if web.svname else self.default_svname}\n"
198
+ """
199
+ web.options.audit_exec(sock, web, body=dict(agent_session=agent_session.id, user_id=user_id, groups=groups, query=query))
200
+ content = types.Content(role='user', parts=[types.Part(text=query)])
201
+
202
+ async for event in web.agent_runner.run_async(user_id=user_id, session_id=agent_session.id, new_message=content):
203
+ #web.agent_runner.session_service.append_event(agent_session, event)
204
+ outputs = dict()
205
+ if event.turn_complete:
206
+ outputs['turn_complete'] = True
207
+ yield json.dumps(outputs, default=common.default_json_enc)
208
+ if event.interrupted:
209
+ outputs['interrupted'] = True
210
+ yield json.dumps(outputs, default=common.default_json_enc)
211
+ #if event.is_final_response():
212
+ msg = None
213
+ if event.content and event.content.parts:
214
+ msg = "\n".join([p.text for p in event.content.parts if p and p.text])
215
+ elif event.actions and event.actions.escalate:
216
+ msg = f"Agent escalated: {event.error_message or 'No specific message.'}"
217
+ if msg:
218
+ msg = json_pattern.sub(_replace_match, msg)
219
+
220
+ outputs['message'] = msg
221
+ web.options.audit_exec(sock, web, body=dict(agent_session=agent_session.id, result=msg))
222
+ yield json.dumps(outputs, default=common.default_json_enc)
223
+ if event.is_final_response():
224
+ break
225
+ except WebSocketDisconnect:
226
+ web.logger.warning('chat: websocket disconnected.')
227
+ break
228
+ except self.SSEDisconnect as e:
229
+ break
230
+ except Exception as e:
231
+ web.logger.warning(f'chat error.', exc_info=True)
232
+ yield json.dumps(dict(message=f'<pre>{traceback.format_exc()}</pre>'), default=common.default_json_enc)
233
+ break
234
+
235
+ def toolmenu(self, web:Web) -> Dict[str, Any]:
236
+ """
237
+ ツールメニューの情報を返します
238
+
239
+ Args:
240
+ web (Web): Webオブジェクト
241
+
242
+ Returns:
243
+ Dict[str, Any]: ツールメニュー情報
244
+
245
+ Sample:
246
+ {
247
+ 'filer': {
248
+ 'html': 'Filer',
249
+ 'href': 'filer',
250
+ 'target': '_blank',
251
+ 'css_class': 'dropdown-item'
252
+ 'onclick': 'alert("filer")'
253
+ }
254
+ }
255
+ """
256
+ return dict(agent=dict(html='Agent', href='agent', target='_blank', css_class='dropdown-item'))
257
+
258
+ class SSEDisconnect(Exception):
259
+ """
260
+ SSEの切断を示す例外クラス
261
+ """
262
+ pass