schift-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- schift_cli/__init__.py +1 -0
- schift_cli/client.py +119 -0
- schift_cli/commands/__init__.py +0 -0
- schift_cli/commands/auth.py +68 -0
- schift_cli/commands/bench.py +65 -0
- schift_cli/commands/catalog.py +74 -0
- schift_cli/commands/db.py +96 -0
- schift_cli/commands/embed.py +104 -0
- schift_cli/commands/migrate.py +127 -0
- schift_cli/commands/query.py +66 -0
- schift_cli/commands/skill.py +110 -0
- schift_cli/commands/usage.py +50 -0
- schift_cli/config.py +58 -0
- schift_cli/data/schift-best-practices/AGENTS.md +77 -0
- schift_cli/data/schift-best-practices/CLAUDE.md +77 -0
- schift_cli/data/schift-best-practices/SKILL.md +89 -0
- schift_cli/data/schift-best-practices/references/bucket-organization.md +126 -0
- schift_cli/data/schift-best-practices/references/bucket-upload.md +116 -0
- schift_cli/data/schift-best-practices/references/chatbot-widget.md +238 -0
- schift_cli/data/schift-best-practices/references/cost-batching.md +179 -0
- schift_cli/data/schift-best-practices/references/cost-storage-tiers.md +183 -0
- schift_cli/data/schift-best-practices/references/deploy-cloudrun.md +140 -0
- schift_cli/data/schift-best-practices/references/embed-batch-processing.md +86 -0
- schift_cli/data/schift-best-practices/references/embed-error-handling.md +155 -0
- schift_cli/data/schift-best-practices/references/embed-multimodal.md +100 -0
- schift_cli/data/schift-best-practices/references/embed-task-types.md +135 -0
- schift_cli/data/schift-best-practices/references/rag-chunking.md +173 -0
- schift_cli/data/schift-best-practices/references/rag-workflow-builder.md +205 -0
- schift_cli/data/schift-best-practices/references/sdk-async-patterns.md +103 -0
- schift_cli/data/schift-best-practices/references/sdk-auth-patterns.md +76 -0
- schift_cli/data/schift-best-practices/references/search-collection-design.md +229 -0
- schift_cli/data/schift-best-practices/references/search-hybrid.md +163 -0
- schift_cli/data/schift-best-practices/references/search-similarity-tuning.md +134 -0
- schift_cli/display.py +85 -0
- schift_cli/main.py +39 -0
- schift_cli-0.1.0.dist-info/METADATA +12 -0
- schift_cli-0.1.0.dist-info/RECORD +40 -0
- schift_cli-0.1.0.dist-info/WHEEL +5 -0
- schift_cli-0.1.0.dist-info/entry_points.txt +2 -0
- schift_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from schift_cli.client import get_client, SchiftAPIError
|
|
6
|
+
from schift_cli.display import console, error, print_table
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command("query")
|
|
10
|
+
@click.argument("text")
|
|
11
|
+
@click.option("--collection", "-c", required=True, help="Collection name to search")
|
|
12
|
+
@click.option("--top-k", "-k", type=int, default=10, show_default=True,
|
|
13
|
+
help="Number of results to return")
|
|
14
|
+
@click.option("--model", "-m", default=None, help="Embedding model to use for the query (uses collection default if omitted)")
|
|
15
|
+
@click.option("--threshold", type=float, default=None, help="Minimum similarity score filter")
|
|
16
|
+
def query(text: str, collection: str, top_k: int, model: str | None, threshold: float | None) -> None:
|
|
17
|
+
"""Search a vector collection with natural language.
|
|
18
|
+
|
|
19
|
+
TEXT is the search query string.
|
|
20
|
+
"""
|
|
21
|
+
payload: dict = {
|
|
22
|
+
"text": text,
|
|
23
|
+
"collection": collection,
|
|
24
|
+
"top_k": top_k,
|
|
25
|
+
}
|
|
26
|
+
if model:
|
|
27
|
+
payload["model"] = model
|
|
28
|
+
if threshold is not None:
|
|
29
|
+
payload["threshold"] = threshold
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
with get_client() as client:
|
|
33
|
+
data = client.post("/query", json=payload)
|
|
34
|
+
except SchiftAPIError as e:
|
|
35
|
+
error(f"Query failed: {e.detail}")
|
|
36
|
+
raise SystemExit(1)
|
|
37
|
+
except click.ClickException:
|
|
38
|
+
raise
|
|
39
|
+
|
|
40
|
+
results = data.get("results", [])
|
|
41
|
+
if not results:
|
|
42
|
+
console.print("No results found.")
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
rows = [
|
|
46
|
+
(
|
|
47
|
+
str(i + 1),
|
|
48
|
+
r.get("id", "-"),
|
|
49
|
+
f"{r.get('score', 0):.4f}",
|
|
50
|
+
_truncate(r.get("text", r.get("metadata", {}).get("text", "-")), 80),
|
|
51
|
+
)
|
|
52
|
+
for i, r in enumerate(results)
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
print_table(
|
|
56
|
+
f"Search Results ({collection})",
|
|
57
|
+
["#", "ID", "Score", "Text"],
|
|
58
|
+
rows,
|
|
59
|
+
caption=f"Query: \"{text}\" | top-k: {top_k}",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _truncate(s: str, max_len: int) -> str:
|
|
64
|
+
if len(s) <= max_len:
|
|
65
|
+
return s
|
|
66
|
+
return s[: max_len - 3] + "..."
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""schift skill install — install Claude Code skills for Schift best practices."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import shutil
|
|
6
|
+
from importlib import resources
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
SKILL_NAME = "schift-best-practices"
|
|
13
|
+
CLAUDE_SKILLS_DIR = Path.home() / ".claude" / "skills"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.group()
|
|
17
|
+
def skill() -> None:
|
|
18
|
+
"""Manage Claude Code skills for Schift."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@skill.command()
|
|
22
|
+
@click.option(
|
|
23
|
+
"--dest",
|
|
24
|
+
type=click.Path(),
|
|
25
|
+
default=None,
|
|
26
|
+
help="Override destination directory (default: ~/.claude/skills/)",
|
|
27
|
+
)
|
|
28
|
+
def install(dest: str | None) -> None:
|
|
29
|
+
"""Install schift-best-practices skill for Claude Code."""
|
|
30
|
+
|
|
31
|
+
target = Path(dest) if dest else CLAUDE_SKILLS_DIR / SKILL_NAME
|
|
32
|
+
|
|
33
|
+
# Locate bundled skill files
|
|
34
|
+
source = _find_skill_source()
|
|
35
|
+
if source is None:
|
|
36
|
+
click.secho(
|
|
37
|
+
"Error: bundled skill files not found. "
|
|
38
|
+
"Try reinstalling schift-cli.",
|
|
39
|
+
fg="red",
|
|
40
|
+
)
|
|
41
|
+
raise SystemExit(1)
|
|
42
|
+
|
|
43
|
+
if target.exists():
|
|
44
|
+
click.confirm(
|
|
45
|
+
f"{target} already exists. Overwrite?",
|
|
46
|
+
abort=True,
|
|
47
|
+
)
|
|
48
|
+
shutil.rmtree(target)
|
|
49
|
+
|
|
50
|
+
shutil.copytree(source, target)
|
|
51
|
+
|
|
52
|
+
# Create CLAUDE.md symlink if missing
|
|
53
|
+
claude_md = target / "CLAUDE.md"
|
|
54
|
+
agents_md = target / "AGENTS.md"
|
|
55
|
+
if agents_md.exists() and not claude_md.exists():
|
|
56
|
+
claude_md.symlink_to("AGENTS.md")
|
|
57
|
+
|
|
58
|
+
click.secho(f"Installed {SKILL_NAME} to {target}", fg="green")
|
|
59
|
+
click.echo(
|
|
60
|
+
"Claude Code will now reference Schift best practices "
|
|
61
|
+
"when you work on embedding, search, or RAG code."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@skill.command()
|
|
66
|
+
def uninstall() -> None:
|
|
67
|
+
"""Remove schift-best-practices skill from Claude Code."""
|
|
68
|
+
|
|
69
|
+
target = CLAUDE_SKILLS_DIR / SKILL_NAME
|
|
70
|
+
if not target.exists():
|
|
71
|
+
click.echo(f"{SKILL_NAME} is not installed.")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
click.confirm(f"Remove {target}?", abort=True)
|
|
75
|
+
shutil.rmtree(target)
|
|
76
|
+
click.secho(f"Removed {SKILL_NAME}", fg="yellow")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@skill.command(name="list")
|
|
80
|
+
def list_skills() -> None:
|
|
81
|
+
"""List installed Schift skills."""
|
|
82
|
+
|
|
83
|
+
target = CLAUDE_SKILLS_DIR / SKILL_NAME
|
|
84
|
+
if target.exists():
|
|
85
|
+
refs = list((target / "references").glob("*.md")) if (target / "references").exists() else []
|
|
86
|
+
click.echo(f"{SKILL_NAME} ({len(refs)} references) {target}")
|
|
87
|
+
else:
|
|
88
|
+
click.echo("No Schift skills installed. Run: schift skill install")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _find_skill_source() -> Path | None:
|
|
92
|
+
"""Locate the bundled skill directory shipped with the package."""
|
|
93
|
+
|
|
94
|
+
# 1. Check package data (installed via pip)
|
|
95
|
+
try:
|
|
96
|
+
pkg = resources.files("schift_cli") / "data" / SKILL_NAME
|
|
97
|
+
# resources.files returns a Traversable; resolve to Path
|
|
98
|
+
pkg_path = Path(str(pkg))
|
|
99
|
+
if pkg_path.is_dir() and (pkg_path / "SKILL.md").exists():
|
|
100
|
+
return pkg_path
|
|
101
|
+
except (TypeError, FileNotFoundError):
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
# 2. Fallback: check relative to repo root (dev mode / editable install)
|
|
105
|
+
repo_root = Path(__file__).resolve().parents[4] # sdk/cli/schift_cli/commands → repo root
|
|
106
|
+
dev_path = repo_root / "skills" / SKILL_NAME
|
|
107
|
+
if dev_path.is_dir() and (dev_path / "SKILL.md").exists():
|
|
108
|
+
return dev_path
|
|
109
|
+
|
|
110
|
+
return None
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from schift_cli.client import get_client, SchiftAPIError
|
|
6
|
+
from schift_cli.display import error, print_kv, print_table
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command("usage")
|
|
10
|
+
@click.option("--period", "-p", default="30d", show_default=True,
|
|
11
|
+
help="Time period (e.g. 7d, 30d, 90d)")
|
|
12
|
+
def usage(period: str) -> None:
|
|
13
|
+
"""Show API usage and billing summary."""
|
|
14
|
+
try:
|
|
15
|
+
with get_client() as client:
|
|
16
|
+
data = client.get("/usage", params={"period": period})
|
|
17
|
+
except SchiftAPIError as e:
|
|
18
|
+
error(f"Failed to fetch usage: {e.detail}")
|
|
19
|
+
raise SystemExit(1)
|
|
20
|
+
except click.ClickException:
|
|
21
|
+
raise
|
|
22
|
+
|
|
23
|
+
summary = data.get("summary", data)
|
|
24
|
+
|
|
25
|
+
print_kv(f"Usage Summary (last {period})", {
|
|
26
|
+
"Total Requests": summary.get("total_requests", "-"),
|
|
27
|
+
"Embeddings Generated": summary.get("embeddings_generated", "-"),
|
|
28
|
+
"Projections Computed": summary.get("projections_computed", "-"),
|
|
29
|
+
"Queries Executed": summary.get("queries_executed", "-"),
|
|
30
|
+
"Storage Used": summary.get("storage_used", "-"),
|
|
31
|
+
"Cost": summary.get("cost", "-"),
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
# Show per-model breakdown if available
|
|
35
|
+
breakdown = data.get("by_model", [])
|
|
36
|
+
if breakdown:
|
|
37
|
+
rows = [
|
|
38
|
+
(
|
|
39
|
+
b.get("model", ""),
|
|
40
|
+
str(b.get("requests", "")),
|
|
41
|
+
str(b.get("tokens", "")),
|
|
42
|
+
b.get("cost", ""),
|
|
43
|
+
)
|
|
44
|
+
for b in breakdown
|
|
45
|
+
]
|
|
46
|
+
print_table(
|
|
47
|
+
"Usage by Model",
|
|
48
|
+
["Model", "Requests", "Tokens", "Cost"],
|
|
49
|
+
rows,
|
|
50
|
+
)
|
schift_cli/config.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
CONFIG_DIR = Path.home() / ".schift"
|
|
9
|
+
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
10
|
+
|
|
11
|
+
ENV_API_KEY = "SCHIFT_API_KEY"
|
|
12
|
+
ENV_API_URL = "SCHIFT_API_URL"
|
|
13
|
+
|
|
14
|
+
DEFAULT_API_URL = "https://api.schift.io/v1"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _ensure_config_dir() -> None:
|
|
18
|
+
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_config() -> dict[str, Any]:
|
|
22
|
+
if not CONFIG_FILE.exists():
|
|
23
|
+
return {}
|
|
24
|
+
try:
|
|
25
|
+
return json.loads(CONFIG_FILE.read_text())
|
|
26
|
+
except (json.JSONDecodeError, OSError):
|
|
27
|
+
return {}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def save_config(config: dict[str, Any]) -> None:
|
|
31
|
+
_ensure_config_dir()
|
|
32
|
+
CONFIG_FILE.write_text(json.dumps(config, indent=2) + "\n")
|
|
33
|
+
# Restrict permissions — API key lives here
|
|
34
|
+
CONFIG_FILE.chmod(0o600)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_api_key() -> str | None:
|
|
38
|
+
"""Return API key from env var (highest priority) or config file."""
|
|
39
|
+
env_key = os.environ.get(ENV_API_KEY)
|
|
40
|
+
if env_key:
|
|
41
|
+
return env_key
|
|
42
|
+
return load_config().get("api_key")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def set_api_key(api_key: str) -> None:
|
|
46
|
+
config = load_config()
|
|
47
|
+
config["api_key"] = api_key
|
|
48
|
+
save_config(config)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def clear_api_key() -> None:
|
|
52
|
+
config = load_config()
|
|
53
|
+
config.pop("api_key", None)
|
|
54
|
+
save_config(config)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_api_url() -> str:
|
|
58
|
+
return os.environ.get(ENV_API_URL, DEFAULT_API_URL)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# schift-best-practices
|
|
2
|
+
|
|
3
|
+
> **Note:** `CLAUDE.md` is a symlink to this file.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Embedding API, vector search, and RAG pipeline best practices from Schift. Use this skill when building embedding-powered applications, integrating Schift SDK, or optimizing vector search and retrieval.
|
|
8
|
+
|
|
9
|
+
## Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
schift-best-practices/
|
|
13
|
+
SKILL.md # Main skill file - read this first
|
|
14
|
+
AGENTS.md # This navigation guide
|
|
15
|
+
CLAUDE.md # Symlink to AGENTS.md
|
|
16
|
+
references/ # Detailed reference files
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
1. Read `SKILL.md` for the main skill instructions
|
|
22
|
+
2. Browse `references/` for detailed documentation on specific topics
|
|
23
|
+
3. Reference files are loaded on-demand - read only what you need
|
|
24
|
+
|
|
25
|
+
## Reference Categories
|
|
26
|
+
|
|
27
|
+
| Priority | Category | Impact | Prefix |
|
|
28
|
+
|----------|----------|--------|--------|
|
|
29
|
+
| 1 | Embedding API | CRITICAL | `embed-` |
|
|
30
|
+
| 2 | Search & Retrieval | CRITICAL | `search-` |
|
|
31
|
+
| 3 | Authentication & SDK | CRITICAL | `sdk-` |
|
|
32
|
+
| 4 | Bucket Management | HIGH | `bucket-` |
|
|
33
|
+
| 5 | RAG Pipelines | HIGH | `rag-` |
|
|
34
|
+
| 6 | Cost Optimization | MEDIUM | `cost-` |
|
|
35
|
+
| 7 | Deployment | LOW-MEDIUM | `deploy-` |
|
|
36
|
+
| 8 | Chatbot Patterns | LOW | `chatbot-` |
|
|
37
|
+
|
|
38
|
+
Reference files are named `{prefix}-{topic}.md` (e.g., `embed-batch-processing.md`).
|
|
39
|
+
|
|
40
|
+
## Available References
|
|
41
|
+
|
|
42
|
+
**Embedding API** (`embed-`):
|
|
43
|
+
- `references/embed-batch-processing.md`
|
|
44
|
+
- `references/embed-multimodal.md`
|
|
45
|
+
- `references/embed-task-types.md`
|
|
46
|
+
- `references/embed-error-handling.md`
|
|
47
|
+
|
|
48
|
+
**Search & Retrieval** (`search-`):
|
|
49
|
+
- `references/search-similarity-tuning.md`
|
|
50
|
+
- `references/search-hybrid.md`
|
|
51
|
+
- `references/search-collection-design.md`
|
|
52
|
+
|
|
53
|
+
**Authentication & SDK** (`sdk-`):
|
|
54
|
+
- `references/sdk-auth-patterns.md`
|
|
55
|
+
- `references/sdk-async-patterns.md`
|
|
56
|
+
|
|
57
|
+
**Bucket Management** (`bucket-`):
|
|
58
|
+
- `references/bucket-upload.md`
|
|
59
|
+
- `references/bucket-organization.md`
|
|
60
|
+
|
|
61
|
+
**RAG Pipelines** (`rag-`):
|
|
62
|
+
- `references/rag-chunking.md`
|
|
63
|
+
- `references/rag-workflow-builder.md`
|
|
64
|
+
|
|
65
|
+
**Cost Optimization** (`cost-`):
|
|
66
|
+
- `references/cost-batching.md`
|
|
67
|
+
- `references/cost-storage-tiers.md`
|
|
68
|
+
|
|
69
|
+
**Deployment** (`deploy-`):
|
|
70
|
+
- `references/deploy-cloudrun.md`
|
|
71
|
+
|
|
72
|
+
**Chatbot Patterns** (`chatbot-`):
|
|
73
|
+
- `references/chatbot-widget.md`
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
*18 reference files across 8 categories*
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# schift-best-practices
|
|
2
|
+
|
|
3
|
+
> **Note:** `CLAUDE.md` is a symlink to this file.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Embedding API, vector search, and RAG pipeline best practices from Schift. Use this skill when building embedding-powered applications, integrating Schift SDK, or optimizing vector search and retrieval.
|
|
8
|
+
|
|
9
|
+
## Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
schift-best-practices/
|
|
13
|
+
SKILL.md # Main skill file - read this first
|
|
14
|
+
AGENTS.md # This navigation guide
|
|
15
|
+
CLAUDE.md # Symlink to AGENTS.md
|
|
16
|
+
references/ # Detailed reference files
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
1. Read `SKILL.md` for the main skill instructions
|
|
22
|
+
2. Browse `references/` for detailed documentation on specific topics
|
|
23
|
+
3. Reference files are loaded on-demand - read only what you need
|
|
24
|
+
|
|
25
|
+
## Reference Categories
|
|
26
|
+
|
|
27
|
+
| Priority | Category | Impact | Prefix |
|
|
28
|
+
|----------|----------|--------|--------|
|
|
29
|
+
| 1 | Embedding API | CRITICAL | `embed-` |
|
|
30
|
+
| 2 | Search & Retrieval | CRITICAL | `search-` |
|
|
31
|
+
| 3 | Authentication & SDK | CRITICAL | `sdk-` |
|
|
32
|
+
| 4 | Bucket Management | HIGH | `bucket-` |
|
|
33
|
+
| 5 | RAG Pipelines | HIGH | `rag-` |
|
|
34
|
+
| 6 | Cost Optimization | MEDIUM | `cost-` |
|
|
35
|
+
| 7 | Deployment | LOW-MEDIUM | `deploy-` |
|
|
36
|
+
| 8 | Chatbot Patterns | LOW | `chatbot-` |
|
|
37
|
+
|
|
38
|
+
Reference files are named `{prefix}-{topic}.md` (e.g., `embed-batch-processing.md`).
|
|
39
|
+
|
|
40
|
+
## Available References
|
|
41
|
+
|
|
42
|
+
**Embedding API** (`embed-`):
|
|
43
|
+
- `references/embed-batch-processing.md`
|
|
44
|
+
- `references/embed-multimodal.md`
|
|
45
|
+
- `references/embed-task-types.md`
|
|
46
|
+
- `references/embed-error-handling.md`
|
|
47
|
+
|
|
48
|
+
**Search & Retrieval** (`search-`):
|
|
49
|
+
- `references/search-similarity-tuning.md`
|
|
50
|
+
- `references/search-hybrid.md`
|
|
51
|
+
- `references/search-collection-design.md`
|
|
52
|
+
|
|
53
|
+
**Authentication & SDK** (`sdk-`):
|
|
54
|
+
- `references/sdk-auth-patterns.md`
|
|
55
|
+
- `references/sdk-async-patterns.md`
|
|
56
|
+
|
|
57
|
+
**Bucket Management** (`bucket-`):
|
|
58
|
+
- `references/bucket-upload.md`
|
|
59
|
+
- `references/bucket-organization.md`
|
|
60
|
+
|
|
61
|
+
**RAG Pipelines** (`rag-`):
|
|
62
|
+
- `references/rag-chunking.md`
|
|
63
|
+
- `references/rag-workflow-builder.md`
|
|
64
|
+
|
|
65
|
+
**Cost Optimization** (`cost-`):
|
|
66
|
+
- `references/cost-batching.md`
|
|
67
|
+
- `references/cost-storage-tiers.md`
|
|
68
|
+
|
|
69
|
+
**Deployment** (`deploy-`):
|
|
70
|
+
- `references/deploy-cloudrun.md`
|
|
71
|
+
|
|
72
|
+
**Chatbot Patterns** (`chatbot-`):
|
|
73
|
+
- `references/chatbot-widget.md`
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
*18 reference files across 8 categories*
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: schift-best-practices
|
|
3
|
+
description: Embedding API, vector search, and RAG pipeline best practices from Schift. Use this skill when building embedding-powered applications, integrating Schift SDK, or optimizing vector search and retrieval.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: schift
|
|
7
|
+
version: "0.1.0"
|
|
8
|
+
organization: Schift
|
|
9
|
+
date: March 2026
|
|
10
|
+
abstract: Comprehensive embedding and vector search best practices for developers using Schift's managed embedding infrastructure. Contains rules across 8 categories covering embedding API usage, search optimization, bucket management, RAG pipelines, SDK patterns, cost optimization, deployment, and chatbot integration. Each rule includes incorrect vs. correct code examples in Python and TypeScript.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Schift Best Practices
|
|
14
|
+
|
|
15
|
+
Best practices for building embedding-powered applications with Schift's managed embedding infrastructure. Covers embedding API, vector search, RAG pipelines, and deployment patterns.
|
|
16
|
+
|
|
17
|
+
## When to Apply
|
|
18
|
+
|
|
19
|
+
Reference these guidelines when:
|
|
20
|
+
- Calling Schift's embedding or search API
|
|
21
|
+
- Designing vector collections or bucket structures
|
|
22
|
+
- Building RAG pipelines with the workflow engine
|
|
23
|
+
- Integrating the Python or TypeScript SDK
|
|
24
|
+
- Deploying Schift-powered applications
|
|
25
|
+
- Optimizing cost and performance for embedding workloads
|
|
26
|
+
- Building chatbot or Q&A interfaces backed by Schift
|
|
27
|
+
|
|
28
|
+
## Rule Categories by Priority
|
|
29
|
+
|
|
30
|
+
| Priority | Category | Impact | Prefix |
|
|
31
|
+
|----------|----------|--------|--------|
|
|
32
|
+
| 1 | Embedding API | CRITICAL | `embed-` |
|
|
33
|
+
| 2 | Search & Retrieval | CRITICAL | `search-` |
|
|
34
|
+
| 3 | Authentication & SDK | CRITICAL | `sdk-` |
|
|
35
|
+
| 4 | Bucket Management | HIGH | `bucket-` |
|
|
36
|
+
| 5 | RAG Pipelines | HIGH | `rag-` |
|
|
37
|
+
| 6 | Cost Optimization | MEDIUM | `cost-` |
|
|
38
|
+
| 7 | Deployment | LOW-MEDIUM | `deploy-` |
|
|
39
|
+
| 8 | Chatbot Patterns | LOW | `chatbot-` |
|
|
40
|
+
|
|
41
|
+
## How to Use
|
|
42
|
+
|
|
43
|
+
Read individual rule files for detailed explanations and code examples:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
references/embed-batch-processing.md
|
|
47
|
+
references/search-similarity-tuning.md
|
|
48
|
+
references/sdk-auth-patterns.md
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Each rule file contains:
|
|
52
|
+
- Brief explanation of why it matters
|
|
53
|
+
- Incorrect code example with explanation
|
|
54
|
+
- Correct code example with explanation
|
|
55
|
+
- Performance impact or cost implications
|
|
56
|
+
- Schift-specific notes
|
|
57
|
+
|
|
58
|
+
## Key Concepts
|
|
59
|
+
|
|
60
|
+
- **Canonical Space**: All embeddings are projected into Schift's 1024-dimensional canonical vector space, enabling cross-model compatibility
|
|
61
|
+
- **Bucket**: File upload → automatic OCR + chunking + embedding → searchable index
|
|
62
|
+
- **Workflow**: DAG-based RAG pipeline builder with block nodes
|
|
63
|
+
- **Task Types**: retrieval, similarity, classification, clustering, QA, fact_verification, code_retrieval
|
|
64
|
+
- **Modalities**: text, image, audio, video, document
|
|
65
|
+
|
|
66
|
+
## SDK Quick Start
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# Python
|
|
70
|
+
pip install schift
|
|
71
|
+
from schift import Schift
|
|
72
|
+
client = Schift(api_key="sch_...")
|
|
73
|
+
result = client.embed("Hello world")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// TypeScript
|
|
78
|
+
npm install @schift-io/sdk
|
|
79
|
+
import { Schift } from '@schift-io/sdk';
|
|
80
|
+
const client = new Schift({ apiKey: 'sch_...' });
|
|
81
|
+
const result = await client.embed('Hello world');
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## References
|
|
85
|
+
|
|
86
|
+
- https://docs.schift.io
|
|
87
|
+
- https://github.com/schift-io/schift
|
|
88
|
+
- https://pypi.org/project/schift/
|
|
89
|
+
- https://www.npmjs.com/package/@schift-io/sdk
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Bucket Organization by Data Domain
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: One bucket per upload session inflates bucket count, fragments the FAISS index, and makes cross-document search impossible without aggregating results manually. Long-lived domain buckets give better search quality and simpler code.
|
|
5
|
+
tags: [bucket, organization, architecture, faiss, search]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Organize Buckets by Data Domain, Not by Upload Session
|
|
9
|
+
|
|
10
|
+
A Schift bucket maintains a single FAISS index over all documents ever uploaded to it. Search over a bucket queries that entire index in one call. When you create a new bucket for every upload session, each bucket becomes a silo — you lose the ability to search across related documents without fanning out to every bucket and merging results yourself.
|
|
11
|
+
|
|
12
|
+
Design buckets around stable data domains: what set of documents belongs together conceptually? That set is a bucket.
|
|
13
|
+
|
|
14
|
+
### Incorrect
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# Python - new bucket per upload session
|
|
18
|
+
# After 10 uploads you have 10 buckets, none searchable together
|
|
19
|
+
from schift import Schift
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
|
|
22
|
+
client = Schift()
|
|
23
|
+
|
|
24
|
+
def upload_weekly_reports(files: list[str]):
|
|
25
|
+
# Creates a new bucket every time this function runs
|
|
26
|
+
bucket = client.bucket.create(f"reports-{datetime.now().isoformat()}")
|
|
27
|
+
job = client.bucket.upload(bucket.id, files=files)
|
|
28
|
+
job.wait()
|
|
29
|
+
return bucket.id
|
|
30
|
+
|
|
31
|
+
# Searching now requires querying every bucket and merging manually
|
|
32
|
+
def search_all_reports(query: str) -> list:
|
|
33
|
+
buckets = client.bucket.list() # grows every week
|
|
34
|
+
all_hits = []
|
|
35
|
+
for b in buckets:
|
|
36
|
+
results = client.bucket.search(b.id, query)
|
|
37
|
+
all_hits.extend(results.hits)
|
|
38
|
+
all_hits.sort(key=lambda h: h.score, reverse=True)
|
|
39
|
+
return all_hits
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// TypeScript - same anti-pattern
|
|
44
|
+
import { Schift } from '@schift-io/sdk';
|
|
45
|
+
|
|
46
|
+
const client = new Schift();
|
|
47
|
+
|
|
48
|
+
async function uploadWeeklyReports(files: string[]) {
|
|
49
|
+
const bucket = await client.bucket.create(`reports-${Date.now()}`);
|
|
50
|
+
const job = await client.bucket.upload(bucket.id, { files });
|
|
51
|
+
await job.wait();
|
|
52
|
+
return bucket.id;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Correct
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
# Python - one bucket per data domain, append files over time
|
|
60
|
+
from schift import Schift
|
|
61
|
+
|
|
62
|
+
client = Schift()
|
|
63
|
+
|
|
64
|
+
# Provision once (e.g., at app startup or in a migration script)
|
|
65
|
+
def get_or_create_bucket(name: str) -> str:
|
|
66
|
+
existing = [b for b in client.bucket.list() if b.name == name]
|
|
67
|
+
if existing:
|
|
68
|
+
return existing[0].id
|
|
69
|
+
return client.bucket.create(name).id
|
|
70
|
+
|
|
71
|
+
# Reuse the same bucket across all weekly uploads
|
|
72
|
+
REPORTS_BUCKET = get_or_create_bucket("weekly-reports")
|
|
73
|
+
|
|
74
|
+
def upload_weekly_reports(files: list[str]):
|
|
75
|
+
job = client.bucket.upload(REPORTS_BUCKET, files=files)
|
|
76
|
+
job.wait()
|
|
77
|
+
|
|
78
|
+
# Single search call covers all documents ever uploaded to this bucket
|
|
79
|
+
def search_reports(query: str):
|
|
80
|
+
return client.bucket.search(REPORTS_BUCKET, query).hits
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// TypeScript - same domain-first approach
|
|
85
|
+
import { Schift } from '@schift-io/sdk';
|
|
86
|
+
|
|
87
|
+
const client = new Schift();
|
|
88
|
+
|
|
89
|
+
async function getOrCreateBucket(name: string): Promise<string> {
|
|
90
|
+
const buckets = await client.bucket.list();
|
|
91
|
+
const existing = buckets.find(b => b.name === name);
|
|
92
|
+
if (existing) return existing.id;
|
|
93
|
+
const bucket = await client.bucket.create(name);
|
|
94
|
+
return bucket.id;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const REPORTS_BUCKET = await getOrCreateBucket('weekly-reports');
|
|
98
|
+
|
|
99
|
+
async function uploadWeeklyReports(files: string[]) {
|
|
100
|
+
const job = await client.bucket.upload(REPORTS_BUCKET, { files });
|
|
101
|
+
await job.wait();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function searchReports(query: string) {
|
|
105
|
+
return (await client.bucket.search(REPORTS_BUCKET, query)).hits;
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Recommended domain examples:**
|
|
110
|
+
|
|
111
|
+
| Domain | Bucket name |
|
|
112
|
+
|--------|-------------|
|
|
113
|
+
| Internal knowledge base | `company-docs` |
|
|
114
|
+
| Product documentation | `product-manuals` |
|
|
115
|
+
| Legal contracts | `legal-contracts` |
|
|
116
|
+
| Support ticket history | `support-history` |
|
|
117
|
+
| Weekly reports | `weekly-reports` |
|
|
118
|
+
|
|
119
|
+
If two domains are always searched together, merge them into one bucket. If they are never searched together and have different access control requirements, keep them separate.
|
|
120
|
+
|
|
121
|
+
Each bucket has an independent FAISS index. Uploads append to the index; the index is never rebuilt from scratch unless explicitly requested.
|
|
122
|
+
|
|
123
|
+
## Reference
|
|
124
|
+
|
|
125
|
+
- https://docs.schift.io/buckets/organization
|
|
126
|
+
- https://docs.schift.io/buckets/faiss-index
|