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.
- seekdb_cli-0.1.0/PKG-INFO +101 -0
- seekdb_cli-0.1.0/README.md +77 -0
- seekdb_cli-0.1.0/pyproject.toml +49 -0
- seekdb_cli-0.1.0/setup.cfg +4 -0
- seekdb_cli-0.1.0/src/seekdb_cli/__init__.py +1 -0
- seekdb_cli-0.1.0/src/seekdb_cli/commands/__init__.py +0 -0
- seekdb_cli-0.1.0/src/seekdb_cli/commands/ai.py +306 -0
- seekdb_cli-0.1.0/src/seekdb_cli/commands/collection.py +188 -0
- seekdb_cli-0.1.0/src/seekdb_cli/commands/data.py +262 -0
- seekdb_cli-0.1.0/src/seekdb_cli/commands/profile.py +142 -0
- seekdb_cli-0.1.0/src/seekdb_cli/commands/query.py +178 -0
- seekdb_cli-0.1.0/src/seekdb_cli/commands/relations.py +174 -0
- seekdb_cli-0.1.0/src/seekdb_cli/commands/schema.py +172 -0
- seekdb_cli-0.1.0/src/seekdb_cli/commands/sql.py +362 -0
- seekdb_cli-0.1.0/src/seekdb_cli/connection.py +454 -0
- seekdb_cli-0.1.0/src/seekdb_cli/logger.py +92 -0
- seekdb_cli-0.1.0/src/seekdb_cli/main.py +266 -0
- seekdb_cli-0.1.0/src/seekdb_cli/masking.py +69 -0
- seekdb_cli-0.1.0/src/seekdb_cli/output.py +203 -0
- seekdb_cli-0.1.0/src/seekdb_cli/vecconnection.py +51 -0
- seekdb_cli-0.1.0/src/seekdb_cli.egg-info/PKG-INFO +101 -0
- seekdb_cli-0.1.0/src/seekdb_cli.egg-info/SOURCES.txt +32 -0
- seekdb_cli-0.1.0/src/seekdb_cli.egg-info/dependency_links.txt +1 -0
- seekdb_cli-0.1.0/src/seekdb_cli.egg-info/entry_points.txt +2 -0
- seekdb_cli-0.1.0/src/seekdb_cli.egg-info/requires.txt +4 -0
- seekdb_cli-0.1.0/src/seekdb_cli.egg-info/top_level.txt +1 -0
- seekdb_cli-0.1.0/tests/test_connection.py +47 -0
- seekdb_cli-0.1.0/tests/test_embedded.py +326 -0
- seekdb_cli-0.1.0/tests/test_logger.py +34 -0
- seekdb_cli-0.1.0/tests/test_masking.py +51 -0
- seekdb_cli-0.1.0/tests/test_output.py +25 -0
- seekdb_cli-0.1.0/tests/test_profile.py +29 -0
- seekdb_cli-0.1.0/tests/test_relations.py +73 -0
- 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 @@
|
|
|
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()
|