seekdb-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. seekdb_cli-0.1.0/PKG-INFO +101 -0
  2. seekdb_cli-0.1.0/README.md +77 -0
  3. seekdb_cli-0.1.0/pyproject.toml +49 -0
  4. seekdb_cli-0.1.0/setup.cfg +4 -0
  5. seekdb_cli-0.1.0/src/seekdb_cli/__init__.py +1 -0
  6. seekdb_cli-0.1.0/src/seekdb_cli/commands/__init__.py +0 -0
  7. seekdb_cli-0.1.0/src/seekdb_cli/commands/ai.py +306 -0
  8. seekdb_cli-0.1.0/src/seekdb_cli/commands/collection.py +188 -0
  9. seekdb_cli-0.1.0/src/seekdb_cli/commands/data.py +262 -0
  10. seekdb_cli-0.1.0/src/seekdb_cli/commands/profile.py +142 -0
  11. seekdb_cli-0.1.0/src/seekdb_cli/commands/query.py +178 -0
  12. seekdb_cli-0.1.0/src/seekdb_cli/commands/relations.py +174 -0
  13. seekdb_cli-0.1.0/src/seekdb_cli/commands/schema.py +172 -0
  14. seekdb_cli-0.1.0/src/seekdb_cli/commands/sql.py +362 -0
  15. seekdb_cli-0.1.0/src/seekdb_cli/connection.py +454 -0
  16. seekdb_cli-0.1.0/src/seekdb_cli/logger.py +92 -0
  17. seekdb_cli-0.1.0/src/seekdb_cli/main.py +266 -0
  18. seekdb_cli-0.1.0/src/seekdb_cli/masking.py +69 -0
  19. seekdb_cli-0.1.0/src/seekdb_cli/output.py +203 -0
  20. seekdb_cli-0.1.0/src/seekdb_cli/vecconnection.py +51 -0
  21. seekdb_cli-0.1.0/src/seekdb_cli.egg-info/PKG-INFO +101 -0
  22. seekdb_cli-0.1.0/src/seekdb_cli.egg-info/SOURCES.txt +32 -0
  23. seekdb_cli-0.1.0/src/seekdb_cli.egg-info/dependency_links.txt +1 -0
  24. seekdb_cli-0.1.0/src/seekdb_cli.egg-info/entry_points.txt +2 -0
  25. seekdb_cli-0.1.0/src/seekdb_cli.egg-info/requires.txt +4 -0
  26. seekdb_cli-0.1.0/src/seekdb_cli.egg-info/top_level.txt +1 -0
  27. seekdb_cli-0.1.0/tests/test_connection.py +47 -0
  28. seekdb_cli-0.1.0/tests/test_embedded.py +326 -0
  29. seekdb_cli-0.1.0/tests/test_logger.py +34 -0
  30. seekdb_cli-0.1.0/tests/test_masking.py +51 -0
  31. seekdb_cli-0.1.0/tests/test_output.py +25 -0
  32. seekdb_cli-0.1.0/tests/test_profile.py +29 -0
  33. seekdb_cli-0.1.0/tests/test_relations.py +73 -0
  34. seekdb_cli-0.1.0/tests/test_sql_parsing.py +84 -0
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: seekdb-cli
3
+ Version: 0.1.0
4
+ Summary: AI-Agent-friendly database CLI for seekdb / OceanBase
5
+ Author: David Zhang
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/oceanbase/seekdb-ecology-plugins
8
+ Project-URL: Repository, https://github.com/oceanbase/seekdb-ecology-plugins
9
+ Project-URL: Issues, https://github.com/oceanbase/seekdb-ecology-plugins/issues
10
+ Keywords: seekdb,database,cli,ai-agent,oceanbase
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Database
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: click>=8.0
21
+ Requires-Dist: pymysql>=1.0
22
+ Requires-Dist: pyseekdb>=0.1
23
+ Requires-Dist: openai>=1.0
24
+
25
+ # seekdb-cli
26
+
27
+ Command-line client for seekdb / OceanBase, built for AI agents. Default JSON output, stateless invocations, and a consistent error format make it easy for agents to run SQL, inspect schema, manage vector collections, and use in-database AI models reliably.
28
+
29
+ ## Why seekdb-cli
30
+
31
+ - **Agent-friendly**: Any agent that can run shell commands can use seekdb-cli via the `seekdb` command; output is JSON by default, and the `seekdb ai-guide` command provides a self-description of seekdb-cli usage for agents.
32
+ - **Safety**: Row limits, write guards, and masking reduce risk when agents or scripts operate on live data.
33
+ - **Unified interface**: One CLI for remote and embedded, SQL and vector collections, plus in-database AI, without interactive prompts or session state.
34
+
35
+ ## Features
36
+
37
+ - **JSON by default**: All commands emit structured JSON; use `--format table|csv|jsonl` for human-readable output.
38
+ - **Row limits**: LIMIT required when result exceeds 100 rows.
39
+ - **Write safeguards**: Writes require `--write`; DELETE/UPDATE without WHERE and DROP/TRUNCATE are disallowed.
40
+ - **Sensitive-field masking**: Columns such as phone, email, password, id_card are auto-masked in query results.
41
+ - **Operation history**: All commands are logged to `~/.seekdb/sql-history.jsonl`; for **SQL execution**, the SQL text is logged with sensitive literals redacted.
42
+ - **Database AI**: Manage models and endpoints via DBMS_AI_SERVICE; use AI_COMPLETE for completion.
43
+
44
+ ## Requirements
45
+
46
+ - Python 3.11+
47
+ - seekdb / OceanBase (or any MySQL-protocol–compatible server)
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ pip install seekdb-cli
53
+ ```
54
+
55
+ After installation, the `seekdb` command is available.
56
+
57
+ ## Connection
58
+
59
+ Set the DSN via environment variable or global option (**global options must appear before the subcommand**):
60
+
61
+ ```bash
62
+ # Remote
63
+ export SEEKDB_DSN="seekdb://user:pass@host:port/database"
64
+
65
+ # Embedded (local directory, no separate server)
66
+ export SEEKDB_DSN="embedded:./seekdb.db"
67
+ export SEEKDB_DSN="embedded:./data?database=mydb"
68
+
69
+ # Or pass per invocation
70
+ seekdb --dsn "seekdb://root:@127.0.0.1:2881/test" status
71
+ ```
72
+
73
+ ## Common commands
74
+
75
+ | Command | Description |
76
+ |--------|-------------|
77
+ | `seekdb status` | Connection status and version |
78
+ | `seekdb schema tables` | List all tables |
79
+ | `seekdb schema describe <table>` | Table structure (columns, types, indexes) |
80
+ | `seekdb schema dump` | Output DDL for all tables (to stdout) |
81
+ | `seekdb table profile <table>` | Table data profile (row count, nulls, distinct, min/max, candidate JOIN keys and time columns) |
82
+ | `seekdb sql "<stmt>"` | Execute SQL (read-only by default; use `--write` for writes; `--with-schema` adds table schema; `--no-truncate` keeps large fields intact) |
83
+ | `seekdb relations infer [--table <t>]` | Infer JOIN relationships between tables |
84
+ | `seekdb collection list \| create \| delete \| info` | Vector collection management |
85
+ | `seekdb query <coll> --text "<query>" [--mode semantic\|fulltext\|hybrid]` | Search a collection |
86
+ | `seekdb get <coll> [--ids ...] [--limit n]` | Get documents by ID or condition |
87
+ | `seekdb add <coll> (--file \| --stdin \| --data)` | Add data to a collection |
88
+ | `seekdb export <coll> --output <path>` | Export collection data |
89
+ | `seekdb ai model list \| create \| delete` | AI model management (DBMS_AI_SERVICE) |
90
+ | `seekdb ai model endpoint create \| delete` | Create or delete AI model endpoints |
91
+ | `seekdb ai complete "<prompt>" --model <name>` | In-database AI completion (AI_COMPLETE) |
92
+ | `seekdb ai-guide` | Print structured guide for AI agents (JSON) |
93
+
94
+ ## Option order
95
+
96
+ `--dsn` and `--format` are global options and must appear **before** the subcommand:
97
+
98
+ ```bash
99
+ seekdb --format table sql "SELECT * FROM t LIMIT 5"
100
+ seekdb --dsn "seekdb://root:@127.0.0.1:2881/test" schema tables
101
+ ```
@@ -0,0 +1,77 @@
1
+ # seekdb-cli
2
+
3
+ Command-line client for seekdb / OceanBase, built for AI agents. Default JSON output, stateless invocations, and a consistent error format make it easy for agents to run SQL, inspect schema, manage vector collections, and use in-database AI models reliably.
4
+
5
+ ## Why seekdb-cli
6
+
7
+ - **Agent-friendly**: Any agent that can run shell commands can use seekdb-cli via the `seekdb` command; output is JSON by default, and the `seekdb ai-guide` command provides a self-description of seekdb-cli usage for agents.
8
+ - **Safety**: Row limits, write guards, and masking reduce risk when agents or scripts operate on live data.
9
+ - **Unified interface**: One CLI for remote and embedded, SQL and vector collections, plus in-database AI, without interactive prompts or session state.
10
+
11
+ ## Features
12
+
13
+ - **JSON by default**: All commands emit structured JSON; use `--format table|csv|jsonl` for human-readable output.
14
+ - **Row limits**: LIMIT required when result exceeds 100 rows.
15
+ - **Write safeguards**: Writes require `--write`; DELETE/UPDATE without WHERE and DROP/TRUNCATE are disallowed.
16
+ - **Sensitive-field masking**: Columns such as phone, email, password, id_card are auto-masked in query results.
17
+ - **Operation history**: All commands are logged to `~/.seekdb/sql-history.jsonl`; for **SQL execution**, the SQL text is logged with sensitive literals redacted.
18
+ - **Database AI**: Manage models and endpoints via DBMS_AI_SERVICE; use AI_COMPLETE for completion.
19
+
20
+ ## Requirements
21
+
22
+ - Python 3.11+
23
+ - seekdb / OceanBase (or any MySQL-protocol–compatible server)
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install seekdb-cli
29
+ ```
30
+
31
+ After installation, the `seekdb` command is available.
32
+
33
+ ## Connection
34
+
35
+ Set the DSN via environment variable or global option (**global options must appear before the subcommand**):
36
+
37
+ ```bash
38
+ # Remote
39
+ export SEEKDB_DSN="seekdb://user:pass@host:port/database"
40
+
41
+ # Embedded (local directory, no separate server)
42
+ export SEEKDB_DSN="embedded:./seekdb.db"
43
+ export SEEKDB_DSN="embedded:./data?database=mydb"
44
+
45
+ # Or pass per invocation
46
+ seekdb --dsn "seekdb://root:@127.0.0.1:2881/test" status
47
+ ```
48
+
49
+ ## Common commands
50
+
51
+ | Command | Description |
52
+ |--------|-------------|
53
+ | `seekdb status` | Connection status and version |
54
+ | `seekdb schema tables` | List all tables |
55
+ | `seekdb schema describe <table>` | Table structure (columns, types, indexes) |
56
+ | `seekdb schema dump` | Output DDL for all tables (to stdout) |
57
+ | `seekdb table profile <table>` | Table data profile (row count, nulls, distinct, min/max, candidate JOIN keys and time columns) |
58
+ | `seekdb sql "<stmt>"` | Execute SQL (read-only by default; use `--write` for writes; `--with-schema` adds table schema; `--no-truncate` keeps large fields intact) |
59
+ | `seekdb relations infer [--table <t>]` | Infer JOIN relationships between tables |
60
+ | `seekdb collection list \| create \| delete \| info` | Vector collection management |
61
+ | `seekdb query <coll> --text "<query>" [--mode semantic\|fulltext\|hybrid]` | Search a collection |
62
+ | `seekdb get <coll> [--ids ...] [--limit n]` | Get documents by ID or condition |
63
+ | `seekdb add <coll> (--file \| --stdin \| --data)` | Add data to a collection |
64
+ | `seekdb export <coll> --output <path>` | Export collection data |
65
+ | `seekdb ai model list \| create \| delete` | AI model management (DBMS_AI_SERVICE) |
66
+ | `seekdb ai model endpoint create \| delete` | Create or delete AI model endpoints |
67
+ | `seekdb ai complete "<prompt>" --model <name>` | In-database AI completion (AI_COMPLETE) |
68
+ | `seekdb ai-guide` | Print structured guide for AI agents (JSON) |
69
+
70
+ ## Option order
71
+
72
+ `--dsn` and `--format` are global options and must appear **before** the subcommand:
73
+
74
+ ```bash
75
+ seekdb --format table sql "SELECT * FROM t LIMIT 5"
76
+ seekdb --dsn "seekdb://root:@127.0.0.1:2881/test" schema tables
77
+ ```
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "seekdb-cli"
7
+ version = "0.1.0"
8
+ description = "AI-Agent-friendly database CLI for seekdb / OceanBase"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "David Zhang"}
14
+ ]
15
+ keywords = ["seekdb", "database", "cli", "ai-agent", "oceanbase"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Database",
24
+ ]
25
+
26
+ dependencies = [
27
+ "click>=8.0",
28
+ "pymysql>=1.0",
29
+ "pyseekdb>=0.1",
30
+ "openai>=1.0",
31
+ ]
32
+
33
+ [project.scripts]
34
+ seekdb = "seekdb_cli.main:cli"
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/oceanbase/seekdb-ecology-plugins"
38
+ Repository = "https://github.com/oceanbase/seekdb-ecology-plugins"
39
+ Issues = "https://github.com/oceanbase/seekdb-ecology-plugins/issues"
40
+
41
+ [tool.setuptools]
42
+ package-dir = {"" = "src"}
43
+
44
+ [tool.setuptools.packages.find]
45
+ where = ["src"]
46
+
47
+ [tool.pytest.ini_options]
48
+ testpaths = ["tests"]
49
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,306 @@
1
+ """seekdb ai command — use database AI (DBMS_AI_SERVICE, AI_COMPLETE)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+
7
+ import click
8
+ import pymysql
9
+
10
+ from seekdb_cli import output
11
+ from seekdb_cli.connection import get_connection
12
+ from seekdb_cli.logger import log_operation
13
+
14
+ # OceanBase AI model types (for CREATE_AI_MODEL)
15
+ AI_MODEL_TYPES = ("dense_embedding", "completion", "rerank")
16
+
17
+
18
+ @click.group("ai")
19
+ @click.pass_context
20
+ def ai_cmd(ctx: click.Context) -> None:
21
+ """AI model management and completion (via database DBMS_AI_SERVICE)."""
22
+
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # seekdb ai model ...
26
+ # ---------------------------------------------------------------------------
27
+
28
+ @ai_cmd.group("model")
29
+ @click.pass_context
30
+ def model_group(ctx: click.Context) -> None:
31
+ """Manage AI models registered in the database (DBMS_AI_SERVICE)."""
32
+
33
+
34
+ @model_group.command("list")
35
+ @click.pass_context
36
+ def model_list(ctx: click.Context) -> None:
37
+ """List all registered AI models (from oceanbase.DBA_OB_AI_MODELS)."""
38
+ fmt: str = ctx.obj["format"]
39
+ dsn: str | None = ctx.obj["dsn"]
40
+
41
+ try:
42
+ conn = get_connection(dsn)
43
+ except Exception as exc:
44
+ log_operation("ai model list", ok=False, error_code="CONNECTION_ERROR")
45
+ output.error("CONNECTION_ERROR", str(exc), fmt=fmt)
46
+ return
47
+
48
+ timer = output.Timer()
49
+ try:
50
+ with timer, conn.cursor() as cur:
51
+ cur.execute(
52
+ "SELECT MODEL_ID, NAME, TYPE, MODEL_NAME "
53
+ "FROM oceanbase.DBA_OB_AI_MODELS ORDER BY NAME"
54
+ )
55
+ rows = cur.fetchall()
56
+ # DictCursor: keys may be uppercase from view
57
+ result = []
58
+ for r in rows:
59
+ row = dict(r)
60
+ result.append({
61
+ "name": row.get("NAME") or row.get("name"),
62
+ "type": row.get("TYPE") or row.get("type"),
63
+ "model_name": row.get("MODEL_NAME") or row.get("model_name"),
64
+ "model_id": row.get("MODEL_ID") or row.get("model_id"),
65
+ })
66
+
67
+ log_operation("ai model list", ok=True, time_ms=timer.elapsed_ms)
68
+ output.success(result, time_ms=timer.elapsed_ms, fmt=fmt)
69
+ except pymysql.err.ProgrammingError as exc:
70
+ log_operation("ai model list", ok=False, error_code="SQL_ERROR")
71
+ output.error(
72
+ "SQL_ERROR",
73
+ str(exc) + ". Ensure the database supports DBA_OB_AI_MODELS (OceanBase AI).",
74
+ fmt=fmt,
75
+ )
76
+ except pymysql.err.Error as exc:
77
+ log_operation("ai model list", ok=False, error_code="SQL_ERROR")
78
+ output.error("SQL_ERROR", str(exc), fmt=fmt)
79
+ finally:
80
+ conn.close()
81
+
82
+
83
+ @model_group.command("create")
84
+ @click.argument("name")
85
+ @click.option(
86
+ "--type",
87
+ "model_type",
88
+ type=click.Choice(AI_MODEL_TYPES),
89
+ required=True,
90
+ help="Model type: dense_embedding, completion, or rerank.",
91
+ )
92
+ @click.option(
93
+ "--model",
94
+ "provider_model_name",
95
+ required=True,
96
+ help="Provider model name (e.g. BAAI/bge-m3, THUDM/GLM-4-9B-0414).",
97
+ )
98
+ @click.pass_context
99
+ def model_create(
100
+ ctx: click.Context,
101
+ name: str,
102
+ model_type: str,
103
+ provider_model_name: str,
104
+ ) -> None:
105
+ """Register an AI model via DBMS_AI_SERVICE.CREATE_AI_MODEL. Create an endpoint separately to use it."""
106
+ fmt: str = ctx.obj["format"]
107
+ dsn: str | None = ctx.obj["dsn"]
108
+
109
+ try:
110
+ conn = get_connection(dsn)
111
+ except Exception as exc:
112
+ log_operation("ai model create", ok=False, error_code="CONNECTION_ERROR")
113
+ output.error("CONNECTION_ERROR", str(exc), fmt=fmt)
114
+ return
115
+
116
+ config = json.dumps({"type": model_type, "model_name": provider_model_name})
117
+ timer = output.Timer()
118
+ try:
119
+ with timer, conn.cursor() as cur:
120
+ cur.execute(
121
+ "CALL DBMS_AI_SERVICE.CREATE_AI_MODEL(%s, %s)",
122
+ (name, config),
123
+ )
124
+ conn.commit()
125
+
126
+ log_operation("ai model create", ok=True, time_ms=timer.elapsed_ms)
127
+ output.success(
128
+ {"name": name, "type": model_type, "model_name": provider_model_name},
129
+ time_ms=timer.elapsed_ms,
130
+ fmt=fmt,
131
+ )
132
+ except pymysql.err.Error as exc:
133
+ log_operation("ai model create", ok=False, error_code="SQL_ERROR")
134
+ output.error("SQL_ERROR", str(exc), fmt=fmt)
135
+ finally:
136
+ conn.close()
137
+
138
+
139
+ @model_group.command("delete")
140
+ @click.argument("name")
141
+ @click.pass_context
142
+ def model_delete(ctx: click.Context, name: str) -> None:
143
+ """Drop an AI model via DBMS_AI_SERVICE.DROP_AI_MODEL. Drop endpoints first if any."""
144
+ fmt: str = ctx.obj["format"]
145
+ dsn: str | None = ctx.obj["dsn"]
146
+
147
+ try:
148
+ conn = get_connection(dsn)
149
+ except Exception as exc:
150
+ log_operation("ai model delete", ok=False, error_code="CONNECTION_ERROR")
151
+ output.error("CONNECTION_ERROR", str(exc), fmt=fmt)
152
+ return
153
+
154
+ timer = output.Timer()
155
+ try:
156
+ with timer, conn.cursor() as cur:
157
+ cur.execute("CALL DBMS_AI_SERVICE.DROP_AI_MODEL(%s)", (name,))
158
+ conn.commit()
159
+
160
+ log_operation("ai model delete", ok=True, time_ms=timer.elapsed_ms)
161
+ output.success({"deleted": name}, time_ms=timer.elapsed_ms, fmt=fmt)
162
+ except pymysql.err.Error as exc:
163
+ log_operation("ai model delete", ok=False, error_code="SQL_ERROR")
164
+ output.error("SQL_ERROR", str(exc), fmt=fmt)
165
+ finally:
166
+ conn.close()
167
+
168
+
169
+ # ---------------------------------------------------------------------------
170
+ # seekdb ai model endpoint ...
171
+ # ---------------------------------------------------------------------------
172
+
173
+ @model_group.group("endpoint")
174
+ @click.pass_context
175
+ def endpoint_group(ctx: click.Context) -> None:
176
+ """Create or delete AI model endpoints (DBMS_AI_SERVICE)."""
177
+
178
+
179
+ @endpoint_group.command("create")
180
+ @click.argument("endpoint_name")
181
+ @click.argument("ai_model_name")
182
+ @click.option("--url", required=True, help="API URL (e.g. https://api.siliconflow.cn/v1/chat/completions).")
183
+ @click.option("--access-key", required=True, help="API key for the service.")
184
+ @click.option("--provider", default="siliconflow", help="Provider name (siliconflow, openai, dashscope, etc.).")
185
+ @click.pass_context
186
+ def endpoint_create(
187
+ ctx: click.Context,
188
+ endpoint_name: str,
189
+ ai_model_name: str,
190
+ url: str,
191
+ access_key: str,
192
+ provider: str,
193
+ ) -> None:
194
+ """Create an AI model endpoint via DBMS_AI_SERVICE.CREATE_AI_MODEL_ENDPOINT."""
195
+ fmt: str = ctx.obj["format"]
196
+ dsn: str | None = ctx.obj["dsn"]
197
+
198
+ try:
199
+ conn = get_connection(dsn)
200
+ except Exception as exc:
201
+ log_operation("ai model endpoint create", ok=False, error_code="CONNECTION_ERROR")
202
+ output.error("CONNECTION_ERROR", str(exc), fmt=fmt)
203
+ return
204
+
205
+ config = json.dumps({
206
+ "ai_model_name": ai_model_name,
207
+ "url": url,
208
+ "access_key": access_key,
209
+ "provider": provider,
210
+ })
211
+ timer = output.Timer()
212
+ try:
213
+ with timer, conn.cursor() as cur:
214
+ cur.execute(
215
+ "CALL DBMS_AI_SERVICE.CREATE_AI_MODEL_ENDPOINT(%s, %s)",
216
+ (endpoint_name, config),
217
+ )
218
+ conn.commit()
219
+
220
+ log_operation("ai model endpoint create", ok=True, time_ms=timer.elapsed_ms)
221
+ output.success(
222
+ {"endpoint": endpoint_name, "ai_model": ai_model_name, "provider": provider},
223
+ time_ms=timer.elapsed_ms,
224
+ fmt=fmt,
225
+ )
226
+ except pymysql.err.Error as exc:
227
+ log_operation("ai model endpoint create", ok=False, error_code="SQL_ERROR")
228
+ output.error("SQL_ERROR", str(exc), fmt=fmt)
229
+ finally:
230
+ conn.close()
231
+
232
+
233
+ @endpoint_group.command("delete")
234
+ @click.argument("endpoint_name")
235
+ @click.pass_context
236
+ def endpoint_delete(ctx: click.Context, endpoint_name: str) -> None:
237
+ """Drop an AI model endpoint via DBMS_AI_SERVICE.DROP_AI_MODEL_ENDPOINT."""
238
+ fmt: str = ctx.obj["format"]
239
+ dsn: str | None = ctx.obj["dsn"]
240
+
241
+ try:
242
+ conn = get_connection(dsn)
243
+ except Exception as exc:
244
+ log_operation("ai model endpoint delete", ok=False, error_code="CONNECTION_ERROR")
245
+ output.error("CONNECTION_ERROR", str(exc), fmt=fmt)
246
+ return
247
+
248
+ timer = output.Timer()
249
+ try:
250
+ with timer, conn.cursor() as cur:
251
+ cur.execute(
252
+ "CALL DBMS_AI_SERVICE.DROP_AI_MODEL_ENDPOINT(%s)",
253
+ (endpoint_name,),
254
+ )
255
+ conn.commit()
256
+
257
+ log_operation("ai model endpoint delete", ok=True, time_ms=timer.elapsed_ms)
258
+ output.success({"deleted": endpoint_name}, time_ms=timer.elapsed_ms, fmt=fmt)
259
+ except pymysql.err.Error as exc:
260
+ log_operation("ai model endpoint delete", ok=False, error_code="SQL_ERROR")
261
+ output.error("SQL_ERROR", str(exc), fmt=fmt)
262
+ finally:
263
+ conn.close()
264
+
265
+
266
+ # ---------------------------------------------------------------------------
267
+ # seekdb ai complete ...
268
+ # ---------------------------------------------------------------------------
269
+
270
+ @ai_cmd.command("complete")
271
+ @click.argument("prompt")
272
+ @click.option("--model", "model_name", required=True, help="Registered completion model name (from ai model list).")
273
+ @click.pass_context
274
+ def ai_complete(ctx: click.Context, prompt: str, model_name: str) -> None:
275
+ """Run text completion using the database AI_COMPLETE function."""
276
+ fmt: str = ctx.obj["format"]
277
+ dsn: str | None = ctx.obj["dsn"]
278
+
279
+ try:
280
+ conn = get_connection(dsn)
281
+ except Exception as exc:
282
+ log_operation("ai complete", ok=False, error_code="CONNECTION_ERROR")
283
+ output.error("CONNECTION_ERROR", str(exc), fmt=fmt)
284
+ return
285
+
286
+ timer = output.Timer()
287
+ try:
288
+ with timer, conn.cursor() as cur:
289
+ cur.execute(
290
+ "SELECT AI_COMPLETE(%s, %s) AS response",
291
+ (model_name, prompt),
292
+ )
293
+ row = cur.fetchone()
294
+ response_text = (row.get("response") or row.get("RESPONSE") or "") if row else ""
295
+
296
+ log_operation("ai complete", ok=True, time_ms=timer.elapsed_ms)
297
+ output.success(
298
+ {"model": model_name, "response": response_text},
299
+ time_ms=timer.elapsed_ms,
300
+ fmt=fmt,
301
+ )
302
+ except pymysql.err.Error as exc:
303
+ log_operation("ai complete", ok=False, error_code="AI_ERROR")
304
+ output.error("AI_ERROR", str(exc), fmt=fmt)
305
+ finally:
306
+ conn.close()