Snippet tools

This commit is contained in:
xzfc
2025-11-26 06:23:25 +00:00
parent 5517016d3b
commit c551dac79f
43 changed files with 2197 additions and 0 deletions
+1
View File
@@ -1,3 +1,4 @@
.idea
__pycache__
qdrant-landing/.DS_Store
.DS_Store
+4
View File
@@ -0,0 +1,4 @@
.gradle
/cache
/clients
/tmp
+116
View File
@@ -0,0 +1,116 @@
# Snippet tools
This is a tool to work with code snippets.
1. Automated CI type checking of snippets.
2. Writing new snippets and checking/running/testing them locally.
This tool hides the complexity of setting up every build system/dev env for each client.
Also, it supports testing snippets against unreleased versions of clients (from git branches/PRs).
## Dependencies
To convert runnable snippets into markdown ([`generate-md.py`](./generate-md.py)) you need only python with no extra dependencies.
To typecheck/build snippets, you need a bunch of tools installed.
The all-in-one [`Dockerfile`](./docker/Dockerfile) and [`shell.nix`](./shell.nix) are provided.
Run [`./docker.sh`](./docker.sh) to start a shell in the docker container.
On ARM-based macOS, ensure that the Docker Desktop setting "General" > "Virtual Machine Options" > "Use Rosetta for x86\_64/amd64 emulation on Apple Silicon" is selected.
Note: on macOS, if you see Java compile failures related to `protobuf`, re-running the test will often solve those.
## Workflow: typechecking and running
Clone and build clients (latest git versions).
```bash
./sync-clients.py fetch --csharp --java --typescript
```
(optional) You might specify particular tags/branches/commits instead.
```bash
./sync-clients.py fetch --csharp=1.16.0 --java=b290b14892534fc0d76893d41b8f656855cd589b --typescript=main
```
(optional) Update the lockfiles for templates (see ./sync-clients.py lock --help for syntax).
```bash
./sync-clients.py lock --go=v1.16.0 --rust=branch=master --python=tag=v1.16.0
```
Typecheck/build all snippets or a particular language/snippet.
```bash
./check.py build
./check.py build -l csharp,java
./check.py build ./snippets/create-collection/simple
./check.py build ./snippets/create-collection/simple/go.go
```
Test all testable snippets against localhost Qdrant instance. (you start it yourself).
Testable snippets are the ones that have `test.py` file in the same directory.
```bash
./check.py test
```
Run a particular snippet without running a test.
```bash
./check.py run ./snippets/create-collection/simple/go.go
```
## Workflow: writing new snippets/converting old ones
Convert existing old markdown snippets.
```bash
./migrate-snippet.py ./snippets/create-collection/simple/go.md
```
Or create new snippets from scratch.
To reduce noise, you can hide boilerplate code using special comment:
```go
if err != nil { panic(err) } // @hide
```
The usual wrapping boileplate like `public static void run() throws Exception {` is already hidden by default.
Typecheck/build the snippet.
```bash
./check.py build ./snippets/create-collection/simple
```
(Optional) Start Qdrant and run the snippet.
```bash
./check.py run ./snippets/create-collection/simple/go.go
```
(Optional) Write `test.py` and test it.
```bash
./check.py test ./snippets/create-collection/simple
```
Before committing, regenerate markdown files.
If migrated, also delete old markdown files.
```bash
./generate-md.py
git rm ./snippets/create-collection/simple/go.md # if migrated
git add ./snippets/create-collection/simple/generated/go.md # made by generate-md.py
git add ./snippets/create-collection/simple/go.go
```
## Architecture
For each language, the snippet tester collects all code snippets from the [`snippets/`](../../qdrant-landing/content/documentation/headless/snippets) directory and puts them in a single scratch project in a temporary directory.
Then those projects are compiled/typechecked.
Putting all snippets in a single project rather than compiling them one by one saves time: some build systems like `gradle` take a lot of time to spin up.
Each supported language has:
- A template project in [`templates/`](./templates) directory.
- A Python subclass of `Language` in [`lib/languages/`](./lib/languages). It is responsible for:
- Creating the scratch project from snippets and the template.
- Building/typechecking the project.
- Running a particular snippet for testing.
- A list of build dependencies in [`docker/Dockerfile`](./docker/Dockerfile) and [`shell.nix`](./shell.nix).
- A handler in [`sync-clients.py`](./sync-clients.py) to fetch the particular git revision of the client.
- For Java, C#, Typescript it fetches the client sources to the `clients/` directory, and builds them.
This directory is gitignored.
- For Go, Rust, Python it updates the lockfile in the [`templates/`](./templates) directory.
These lockfiles are checked in this repo.
+174
View File
@@ -0,0 +1,174 @@
#!/usr/bin/env python3
import argparse
import importlib.util
import shutil
import subprocess
import sys
import traceback
import types
import typing
from pathlib import Path
from lib import SNIPPETS_DIR, CollectedSnippetsType, collect_snippets, log
from lib.languages import ALL_LANGUAGES, CompileResult, Language, parse_languages
def main() -> None:
parser = argparse.ArgumentParser()
default_langs = ",".join([lang.NAME for lang in ALL_LANGUAGES])
parser.add_argument(
"-l",
"--langs",
help=f"comma-separated list of languages, default is {default_langs}",
default=default_langs,
)
parser.add_argument(
"command",
help="command to execute",
choices=["build", "run", "test"],
)
parser.add_argument(
"snippets",
nargs="*",
help=f"Snippet filenames/directories to process, default: {SNIPPETS_DIR}",
default=[SNIPPETS_DIR],
type=Path,
metavar="SNIPPET_PATH",
)
args = parser.parse_args()
languages = parse_languages(args.langs)
snippets = collect_snippets(args.snippets, languages)
assert args.command in ("build", "run", "test")
# Q: Why not `tempfile.TemporaryDirectory()`?
# A: Harder to debug/inspect generated files.
tmpdir = Path(__file__).parent / "tmp"
shutil.rmtree(tmpdir, ignore_errors=True)
tmpdir.mkdir(parents=True)
build_and_run(tmpdir=tmpdir, snippets=snippets, mode=args.command)
def build_and_run(
tmpdir: Path,
snippets: CollectedSnippetsType,
mode: typing.Literal[
"build", # just build
"run", # build and run each snippet
"test", # build, then run/test all snippets that have a test.py file
],
) -> None:
snippets_by_lang: dict[type[Language], list[Path]] = {}
for snippets2 in snippets.values():
for lang, fname in snippets2.items():
snippets_by_lang.setdefault(lang, []).append(fname)
compile_results: dict[type[Language], CompileResult] = {}
errors: list[str] = []
# Load test modules before compilation.
# We want them to crash early.
test_modules: dict[Path, types.ModuleType] = {}
if mode == "test":
for snippet_dir in snippets:
test_file = snippet_dir / "test.py"
if not test_file.exists():
continue
spec = importlib.util.spec_from_file_location("test_module", test_file)
assert spec is not None
mod = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(mod)
test_modules[snippet_dir] = mod
log("Compile stage")
for lang, fnames in snippets_by_lang.items():
log(f"· Compiling {lang.NAME} snippets")
try:
res = lang.compile(tmpdir / lang.NAME, fnames)
if res.has_issues:
log(f"· · Compilation had issues for {lang.NAME}")
errors.append(f"Compilation had issues for {lang.NAME}")
compile_results[lang] = res
except Exception as e:
log(f"· · Compilation failed for {lang.NAME}")
errors.append(f"Compilation failed for {lang.NAME}")
if not isinstance(e, subprocess.CalledProcessError):
traceback.print_exc()
if mode in ("run", "test"):
log("Run stage")
for snippet_dir, snippets2 in snippets.items():
if mode == "run":
for lang, snippet_fname in snippets2.items():
if (compile_result := compile_results.get(lang)) is None:
continue
log(f"· Running {snippet_fname}")
p = subprocess.run(
compile_result.run_args[snippet_fname],
text=True,
)
if p.returncode != 0:
log(f"· · Exit code {p.returncode}")
if mode == "test" and snippet_dir in test_modules:
log(f"· Testing snippets in {snippet_dir}")
mod = test_modules[snippet_dir]
for lang, snippet_fname in snippets2.items():
compile_result = compile_results.get(lang)
if compile_result is None:
continue
log(f"· · Testing {snippet_fname}")
output = None
try:
if hasattr(mod, "prepare"):
mod.prepare()
p = subprocess.run(
compile_result.run_args[snippet_fname],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
output = p.stdout
if p.returncode != 0:
msg = f"Process exited with code {p.returncode}"
raise RuntimeError(msg)
if hasattr(mod, "check"):
mod.check()
except Exception:
log(f"· · · Testing {snippet_fname} failed")
if output:
print(output.rstrip())
traceback.print_exc()
errors.append(f"Testing {snippet_fname} failed")
finally:
try:
if hasattr(mod, "cleanup"):
mod.cleanup()
except Exception:
log(f"· · · Teardown for {snippet_fname} failed")
errors.append(f"Teardown for {snippet_fname} failed")
traceback.print_exc()
if errors:
log("Errors encountered:")
for err in errors:
log(f"· {err}")
sys.exit(1)
elif mode == "test":
log("All tests passed successfully.")
if __name__ == "__main__":
main()
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env sh
set -e
IMAGE_NAME="snippet-checker"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
# check if on macos
if ! docker image inspect "$IMAGE_NAME" > /dev/null 2>&1; then
# TODO: comment why
[ "$(uname)" != Darwin ] || export DOCKER_DEFAULT_PLATFORM=linux/amd64
docker build -t "$IMAGE_NAME" "$SCRIPT_DIR/docker"
fi
[ $# -ne 0 ] || set -- bash
[ -t 0 ] && TTY_FLAG=--tty || TTY_FLAG=
docker run \
--rm \
--interactive \
$TTY_FLAG \
--network host \
--user "$(id -u):$(id -g)" \
--volume "$REPO_ROOT_DIR:/workspace" \
"$IMAGE_NAME" \
uv run "$@"
+51
View File
@@ -0,0 +1,51 @@
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
software-properties-common \
# for rust client
gcc \
libc6-dev \
# for go client
golang \
# for java client
openjdk-21-jdk-headless \
# for python client
python3 \
# for typescript client
nodejs \
npm \
&& rm -rf /var/lib/apt/lists/*
# for csharp client
RUN add-apt-repository ppa:dotnet/backports && \
apt-get update && \
apt-get install -y dotnet-sdk-8.0 --no-install-recommends && \
rm -rf /var/lib/apt/lists/*
# for rust client
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
CARGO_HOME=/opt/rust RUSTUP_HOME=/opt/rust \
sh -s -- -y --default-toolchain 1.91.0
# for typescript client
RUN npm install --global --prefix /usr/local pnpm
# Without this, typescript snippets tries to connect to ::1 instead of 127.1
RUN echo "precedence ::ffff:0:0/96 100" >> /etc/gai.conf
# for python client
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
ENV RUSTUP_HOME=/opt/rust \
PATH="/opt/rust/bin:${PATH}"
# /workspace is the repo root
WORKDIR /workspace/automation/snippets
# Build tools put their caches in $HOME
ENV HOME=/workspace/automation/snippets/cache \
# gradle ignores $HOME
GRADLE_USER_HOME=/workspace/automation/snippets/cache/.gradle
ENTRYPOINT ["/bin/bash", "-c", "mkdir -p $HOME && \"$@\"", "--"]
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""Converts all runnable snippets to markdown files.
E.g., converts
`snippets/create-collection/simple/python.py`
to
`snippets/create-collection/simple/generated/python.md`
For using on CI and running before committing changes.
The reverse is `./migrate-snippet.py`.
"""
import difflib
import shutil
import sys
import traceback
import typing
from lib import SNIPPETS_DIR, collect_snippets
from lib.languages import ALL_LANGUAGES
def main() -> None:
snippets = collect_snippets(bases=[SNIPPETS_DIR], languages=ALL_LANGUAGES)
issues = 0
for snippet_dir, snippets2 in snippets.items():
generated_dir = snippet_dir / "generated"
shutil.rmtree(generated_dir, ignore_errors=True)
generated_dir.mkdir()
for lang, snippet_fname in snippets2.items():
try:
shortened = lang.shorten(snippet_fname.read_text())
except Exception as e:
issues += 1
print(f"Warning: failed to shorten snippet {snippet_fname}: {e}")
traceback.print_exc()
continue
generated = f"```{lang.NAME}\n{shortened}```\n"
generated_fname = generated_dir / f"{lang.NAME}.md"
handwritten_fname = snippet_dir / f"{lang.NAME}.md"
generated_fname.write_text(generated)
if handwritten_fname.exists():
issues += 1
print(
"Warning: both snippet and generated file exist:",
snippet_dir / f"{lang.NAME}.md",
)
handwritten = (snippet_dir / f"{lang.NAME}.md").read_text()
if handwritten != generated:
print_and_colorize_diff(
difflib.unified_diff(
handwritten.splitlines(keepends=True),
generated.splitlines(keepends=True),
fromfile=str(handwritten_fname),
tofile=str(generated_fname),
),
)
print()
if issues:
print(f"Total issues found: {issues}")
sys.exit(1)
def print_and_colorize_diff(diff: typing.Iterable[str]) -> None:
for line in diff:
if line.startswith("+"):
print(end=f"\x1b[32m{line}\x1b[0m", file=sys.stderr)
elif line.startswith("-"):
print(end=f"\x1b[31m{line}\x1b[0m", file=sys.stderr)
elif line.startswith("@"):
print(end=f"\x1b[36m{line}\x1b[0m", file=sys.stderr)
else:
print(end=line, file=sys.stderr)
if __name__ == "__main__":
main()
+74
View File
@@ -0,0 +1,74 @@
import os
import sys
import typing
from pathlib import Path
from .languages import Language
SNIPPETS_DIR = Path(os.path.normpath(Path(__file__) / "../../snippets")).relative_to(
Path.cwd()
)
def log(msg: str) -> None:
"""Print a message.
Use this instead of `print()` to make the output more visible compared to
the output of builders/snippet runs.
"""
print(f"\x1b[30;43m + \x1b[m {msg}", file=sys.stderr)
type CollectedSnippetsType = dict[
Path, # directory path
dict[type[Language], Path], # language -> snippet path
]
"""Snippet filenames grouped by directory then by language.
Example:
{
Path("snippets/example1"): {
LanguageGo: Path("snippets/example1/go.go"),
LanguagePython: Path("snippets/example1/python.py"),
},
Path("snippets/example2"): {
LanguageGo: Path("snippets/example2/go.go"),
LanguagePython: Path("snippets/example2/python.py"),
LanguageRust: Path("snippets/example2/rust.rs"),
},
}
"""
def collect_snippets(
bases: list[Path],
languages: typing.Sequence[type[Language]],
) -> CollectedSnippetsType:
"""Locate snippets in the given base paths."""
result: CollectedSnippetsType = {}
for base in bases:
found = False
if base.is_file():
for lang in languages:
if base.name == lang.SNIPPET_FILENAME:
result.setdefault(base.parent, {})[lang] = base
found = True
break
elif base.is_dir():
for root, dirs, files in os.walk(base):
dirs[:] = [d for d in dirs if d != "__pycache__"]
dirs.sort()
dir_path = Path(root)
for lang in languages:
if lang.SNIPPET_FILENAME in files:
found = True
result.setdefault(dir_path, {})[lang] = (
dir_path / lang.SNIPPET_FILENAME
)
if not found:
log(f"Warning: No snippets found in {base}")
return result
@@ -0,0 +1,53 @@
from .base import CompileResult, Language
from .bash import LanguageBash
from .csharp import LanguageCsharp
from .go import LanguageGo
from .java import LanguageJava
from .python import LanguagePython
from .rust import LanguageRust
from .typescript import LanguageTypescript
__all__ = [
"ALL_LANGUAGES",
"CompileResult",
"Language",
"LanguageBash",
"LanguageCsharp",
"LanguageGo",
"LanguageJava",
"LanguagePython",
"LanguageRust",
"LanguageTypescript",
"parse_languages",
]
ALL_LANGUAGES: tuple[type[Language], ...] = (
LanguageBash,
LanguageCsharp,
LanguageGo,
LanguageJava,
LanguagePython,
LanguageRust,
LanguageTypescript,
)
def parse_languages(langs: str) -> list[type[Language]]:
"""Parse comma-separated list of languages into a list of Language classes.
Accepts either language names or snippet file extensions.
Example: "python,ts,go" -> [LanguagePython, LanguageTypescript, LanguageGo]
"""
result: list[type[Language]] = []
for lang_name in langs.split(","):
lang_name = lang_name.strip()
for lang in ALL_LANGUAGES:
if lang in result:
continue
if lang_name in (lang.NAME, lang.SNIPPET_FILENAME.split(".")[1]):
result.append(lang)
break
else:
msg = f"Unknown language: {lang_name}"
raise ValueError(msg)
return result
+121
View File
@@ -0,0 +1,121 @@
import dataclasses
import os
import re
import typing
from pathlib import Path
if typing.TYPE_CHECKING:
from _typeshed import StrOrBytesPath
class Language:
"""Base class for a programming language snippet tester."""
NAME: str
"""Name of the programming language, e.g., 'python'."""
SNIPPET_FILENAME: str
"""Filename of the snippet in this language, e.g., 'python.py'."""
@classmethod
def compile(cls, tmpdir: Path, fnames: list[Path]) -> "CompileResult":
"""Compile, typecheck, and prepare all snippets listed in fnames.
Might create scratch projects in tmpdir.
"""
raise NotImplementedError
@classmethod
def shorten(cls, contents: str) -> str:
"""Shorten the snippet contents into a form suitable for inclusion in
documentation.
Removes boilerplate code, e.g. class wrappers, main functions, etc.
"""
return generic_shorten(contents)
@classmethod
def unshorten(cls, contents: str) -> str:
"""Convert the shortened snippet into runnable code.
Not reliable, might require manual adjustments.
"""
return contents
@classmethod
def format(cls, fnames: list[str]) -> None:
"""Format the snippets specified by fnames in place."""
pass
@dataclasses.dataclass
class CompileResult:
run_args: dict[Path, typing.Sequence["StrOrBytesPath"]] = dataclasses.field(
default_factory=dict
)
"""Mapping from the snippet filename to the command that runs the
snippet."""
has_issues: bool = False
"""If true, indicates that there were issues during
compilation/typechecking, but the snippets can still be run.
"""
def copy_template(src: Path, dst: Path) -> None:
dst.parent.mkdir(parents=True, exist_ok=True)
dst.write_text(src.read_text().replace("{collection_name}", "collection_name"))
def template(
original_fname: Path,
target_fname: Path,
replacements: dict[str, str],
) -> None:
result = []
for line in original_fname.read_text().splitlines(keepends=True):
if (r := replacements.get(line.strip())) is not None:
result.append(r + "\n")
else:
result.append(line)
target_fname.write_text("".join(result))
_RE_COMMENT = re.compile(r".*\s(?://|#)\s*(@.*)$")
def generic_shorten(text: str) -> str:
"""Generic implementation of Language.shorten().
Removes comments with @hide annotation and trims excessive newlines.
"""
result = []
for line in text.splitlines():
if (m := _RE_COMMENT.match(line)) is not None:
if m[1] == "@hide":
continue
else:
msg = f"Unknown annotation: {m[1]}"
raise ValueError(msg)
result.append(line + "\n")
text = "".join(result)
text = text.lstrip("\n").rstrip("\n")
text = re.sub(r"\n{3,}", "\n\n", text)
if text:
text += "\n"
return text
def trim_commonpath(fnames: list[Path]) -> dict[Path, Path]:
"""Trim the common path prefix from a list of file paths.
Returns a mapping from original paths to trimmed paths.
"""
common_prefix = os.path.commonpath(
[(Path.cwd() / fname.parent) for fname in fnames]
)
result = {}
for fname in fnames:
trimmed = Path(os.path.relpath(fname, common_prefix))
result[fname] = trimmed
return result
+17
View File
@@ -0,0 +1,17 @@
from pathlib import Path
from .base import CompileResult, Language, copy_template
class LanguageBash(Language):
NAME = "bash"
SNIPPET_FILENAME = "bash.sh"
@classmethod
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
tmpdir.mkdir(parents=True)
result = CompileResult()
for no, fname in enumerate(fnames):
copy_template(fname, tmpdir / f"snippet{no}.sh")
result.run_args[fname] = ["bash", "-ex", f"{tmpdir}/snippet{no}.sh"]
return result
+125
View File
@@ -0,0 +1,125 @@
import re
import shutil
import subprocess
import textwrap
from pathlib import Path
from .base import CompileResult, Language, generic_shorten, template, trim_commonpath
class LanguageCsharp(Language):
NAME = "csharp"
SNIPPET_FILENAME = "csharp.cs"
@classmethod
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
shutil.copytree("templates/csharp", tmpdir)
csproj_path = tmpdir / "csharp.csproj"
csproj_path.write_text(
csproj_path.read_text().replace(
"../../clients/csharp/",
f"{Path('clients/csharp').absolute()}/",
)
)
trimmed_fnames = trim_commonpath(fnames)
src_cases = []
result = CompileResult()
for no, (snippet_fname, trimmed_fname) in enumerate(trimmed_fnames.items()):
content = (
snippet_fname.read_text()
.replace("public class Snippet", f"public class Snippet{no}")
.replace("{collection_name}", "collection_name")
)
dest_path = tmpdir / "s" / trimmed_fname.parent / f"Snippet{no}.cs"
dest_path.parent.mkdir(parents=True, exist_ok=True)
dest_path.write_text(content)
src_cases.append(
f'case "{snippet_fname}": await Snippet{no}.Run(); break;\n'
)
result.run_args[snippet_fname] = [
"dotnet",
f"{tmpdir}/bin/snippets-amalgamation.dll",
snippet_fname,
]
template(
original_fname=Path("templates/csharp/Program.cs"),
target_fname=tmpdir / "Program.cs",
replacements={"// %cases%": "".join(src_cases)},
)
subprocess.run(["dotnet", "build", "-o", "bin"], cwd=tmpdir, check=True)
return result
RE_CODE = re.compile(
r"""
(?P<imports> .*? )
\s*public\ class\ Snippet\s*\{\s*
public\ static\ async\ Task\ Run\(\)\s*\{\n
(?P<body> .*? )
\s*\}\s*}\s*
$
""",
re.DOTALL | re.VERBOSE,
)
EXAMPLE_CODE = textwrap.dedent(
"""\
using Qdrant.Client;
using Qdrant.Client.Grpc;
public class Snippet
{
public static async Task Run()
{
// Your code here
}
}
"""
)
assert RE_CODE.match(EXAMPLE_CODE) is not None
@classmethod
def shorten(cls, contents: str) -> str:
if (m := LanguageCsharp.RE_CODE.match(contents)) is None:
msg = "Invalid snippet format"
raise ValueError(msg)
return generic_shorten(
m["imports"].strip() + "\n\n" + textwrap.dedent(m["body"]).strip()
)
RE_RENDERED = re.compile(
r"""
(?P<imports> (?:using\s[^;]+;\n|\n)* )
(?P<body> .* )
$
""",
re.DOTALL | re.VERBOSE,
)
@classmethod
def unshorten(cls, contents: str) -> str:
if m := LanguageCsharp.RE_RENDERED.match(contents):
return textwrap.dedent(
"""\
{imports}
public class Snippet
{{
\tpublic static async Task Run()
\t{{
{body}
\t}}
}}
"""
).format(
imports=m["imports"].strip(),
body=textwrap.indent(m["body"].strip(), "\t" * 2),
)
return contents
+119
View File
@@ -0,0 +1,119 @@
import re
import shutil
import subprocess
import textwrap
from pathlib import Path
from .base import (
CompileResult,
Language,
copy_template,
generic_shorten,
template,
trim_commonpath,
)
class LanguageGo(Language):
NAME = "go"
SNIPPET_FILENAME = "go.go"
@classmethod
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
shutil.copytree("templates/go", tmpdir)
trimmed_fnames = trim_commonpath(fnames)
src_imports = []
src_cases = []
result = CompileResult()
for no, (snippet_fname, trimmed_fname) in enumerate(trimmed_fnames.items()):
dest_path = tmpdir / "s" / trimmed_fname
copy_template(snippet_fname, dest_path)
src_imports.append(
f's{no} "example.com/snippets-amalgamation/{("s" / trimmed_fname).parent}"'
)
src_cases.append(f'case "{snippet_fname}": s{no}.Main()\n')
result.run_args[snippet_fname] = [tmpdir / "tester", snippet_fname]
template(
original_fname=Path("templates/go/main.go"),
target_fname=tmpdir / "main.go",
replacements={
"// %imports%": "\n".join(src_imports),
"// %cases%": "\n".join(src_cases),
},
)
subprocess.run(["go", "build", "-o", "tester", "."], cwd=tmpdir, check=True)
return result
RE_CODE = re.compile(
r"""
package\ [a-zA-Z0-9_]+\n
(?P<imports> .*? )
func\ Main\(\)\ \{\n
(?P<body> .*? )
\}\s*$
""",
re.DOTALL | re.VERBOSE,
)
EXAMPLE_CODE = textwrap.dedent(
"""\
package main
import (
"github.com/qdrant/go-client/qdrant"
)
func Main() {
// Your code here
}
"""
)
assert RE_CODE.match(EXAMPLE_CODE) is not None
@classmethod
def shorten(cls, contents: str) -> str:
if (m := LanguageGo.RE_CODE.match(contents)) is None:
msg = "Invalid snippet format"
raise ValueError(msg)
return generic_shorten(
m["imports"].strip() + "\n\n" + textwrap.dedent(m["body"]).strip()
)
@classmethod
def format(cls, fnames: list[str]) -> None:
subprocess.run(["gofmt", "-w", *fnames], check=True)
RE_RENDERED = re.compile(
r"""
(?P<imports> (?:import\s*\([^)]+\)\n|\n)* )
(?P<body> .* )
$
""",
re.DOTALL | re.VERBOSE,
)
@classmethod
def unshorten(cls, contents: str) -> str:
if m := LanguageGo.RE_RENDERED.match(contents):
return textwrap.dedent(
"""\
package snippet
{imports}
func Main() {{
{body}
}}
"""
).format(
imports=m["imports"].strip(),
body=textwrap.indent(m["body"].strip(), "\t"),
)
return contents
+128
View File
@@ -0,0 +1,128 @@
import os
import re
import shutil
import subprocess
import textwrap
from pathlib import Path
from .base import CompileResult, Language, generic_shorten, template, trim_commonpath
class LanguageJava(Language):
NAME = "java"
SNIPPET_FILENAME = "java.java"
@classmethod
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
(tmpdir / "src/main/java/s").mkdir(parents=True)
shutil.copyfile("templates/java/build.gradle", f"{tmpdir}/build.gradle")
shutil.copyfile("templates/java/settings.gradle", f"{tmpdir}/settings.gradle")
trimmed_fnames = trim_commonpath(fnames)
result = CompileResult()
src_cases = []
for no, (snippet_fname, trimmed_fname) in enumerate(trimmed_fnames.items()):
content = (
snippet_fname.read_text()
.replace("public class Snippet", f"public class Snippet{no}")
.replace("{collection_name}", "collection_name")
)
dest_path = (
tmpdir / "src/main/java/s" / trimmed_fname.parent / f"Snippet{no}.java"
)
dest_path.parent.mkdir(parents=True, exist_ok=True)
dest_path.write_text(content)
src_cases.append(f'case "{snippet_fname}": Snippet{no}.run(); break;\n')
result.run_args[snippet_fname] = [
"java",
"-jar",
f"{tmpdir}/build/libs/app-1.0.0-all.jar",
snippet_fname,
]
template(
original_fname=Path("templates/java/src/main/java/Main.java"),
target_fname=tmpdir / "src/main/java/Main.java",
replacements={
"// %cases%": "\n".join(src_cases),
},
)
gradlew = os.path.abspath("templates/java/gradlew")
client = os.path.abspath("clients/java")
subprocess.run(
[gradlew, "--no-daemon", f"-PclientPath={client}", "shadowJar"],
cwd=tmpdir,
check=True,
)
return result
RE_CODE = re.compile(
r"""
package\s[a-zA-Z0-9_.]+;\n
(?P<imports> .*? )
\s*public\ class\ Snippet\ \{\n
\s*public\ static\ void\ run\(\)\ throws\ Exception\ \{\n
(?P<body> .*? )
\s*\}\s*}\s*
$
""",
re.DOTALL | re.VERBOSE,
)
EXAMPLE_CODE = textwrap.dedent(
"""\
package com.example.snippets_amalgamation;
import io.qdrant.client.QdrantClient;
public class Snippet {
public static void run() throws Exception {
// Your code here
}
}
"""
)
assert RE_CODE.match(EXAMPLE_CODE) is not None
@classmethod
def shorten(cls, contents: str) -> str:
if (m := LanguageJava.RE_CODE.match(contents)) is None:
msg = "Invalid snippet format"
raise ValueError(msg)
return generic_shorten(
m["imports"].strip() + "\n\n" + textwrap.dedent(m["body"]).strip()
)
RE_RENDERED = re.compile(
r"""
(?P<imports> (?: import\s[^;]+;\n|\n)* )
(?P<body> .* )
$
""",
re.DOTALL | re.VERBOSE,
)
@classmethod
def unshorten(cls, contents: str) -> str:
if m := LanguageJava.RE_RENDERED.match(contents):
return textwrap.dedent(
"""\
package com.example.snippets_amalgamation;
{imports}
public class Snippet {{
public static void run() throws Exception {{
{body}
}}
}}
"""
).format(
imports=m["imports"].strip(),
body=textwrap.indent(m["body"].strip(), " " * 16),
)
return contents
@@ -0,0 +1,34 @@
import subprocess
from pathlib import Path
from .base import CompileResult, Language, copy_template, trim_commonpath
class LanguagePython(Language):
NAME = "python"
SNIPPET_FILENAME = "python.py"
@classmethod
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
trimmed_fnames = trim_commonpath(fnames)
result = CompileResult()
for snippet_fname, trimmed_fname in trimmed_fnames.items():
target_path = tmpdir / str(trimmed_fname).replace("-", "_")
target_path.parent.mkdir(parents=True, exist_ok=True)
dir = tmpdir
for component in Path(str(trimmed_fname).replace("-", "_")).parts:
(dir / "__init__.py").touch()
dir /= component
copy_template(snippet_fname, target_path)
result.run_args[snippet_fname] = [
"uv",
"--project=templates/python",
"run",
target_path,
]
p = subprocess.run(["uv", "--project=templates/python", "run", "mypy", tmpdir])
result.has_issues = p.returncode != 0
return result
+128
View File
@@ -0,0 +1,128 @@
import os
import re
import shutil
import subprocess
import textwrap
from pathlib import Path
from .base import (
CompileResult,
Language,
copy_template,
generic_shorten,
template,
trim_commonpath,
)
class LanguageRust(Language):
NAME = "rust"
SNIPPET_FILENAME = "rust.rs"
@classmethod
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
tmpdir.mkdir()
(tmpdir / "src").mkdir()
shutil.copyfile("templates/rust/Cargo.toml", f"{tmpdir}/Cargo.toml")
shutil.copyfile("templates/rust/Cargo.lock", f"{tmpdir}/Cargo.lock")
src_mods = []
src_cases = []
trimmed_fnames = trim_commonpath(fnames)
result = CompileResult()
binary_path = Path.cwd() / "templates/rust/target/debug/snippets-amalgamation"
for no, (snippet_fname, trimmed_fname) in enumerate(trimmed_fnames.items()):
copy_template(snippet_fname, tmpdir / "src" / "s" / trimmed_fname)
src_mods.append(f'#[path = "s/{trimmed_fname}"] mod s{no};')
src_cases.append(f'"{snippet_fname}" => s{no}::main().await.unwrap(),\n')
result.run_args[snippet_fname] = [binary_path, snippet_fname]
template(
original_fname=Path("templates/rust/src/main.rs"),
target_fname=tmpdir / "src/main.rs",
replacements={
"// %mods%": "\n".join(src_mods),
"// %cases%": "\n".join(src_cases),
},
)
subprocess.run(
["cargo", "build"],
cwd=tmpdir,
env={
**os.environ,
"CARGO_TARGET_DIR": str(Path.cwd() / "templates/rust/target"),
},
check=True,
)
return result
@classmethod
def format(cls, fnames: list[str]) -> None:
subprocess.run(["rustfmt", "--edition=2024", *fnames], check=True)
RE_CODE = re.compile(
r"""
(?P<uses> (?:use\s[^;]+;\n|\n)* )
pub\s+async\s+fn\s+main\(\)\s*->\s*anyhow::Result<\(\)>\s*\{\n
(?P<body> .*? )
\s*Ok\(\(\)\)\n
\}\n?
$
""",
re.DOTALL | re.VERBOSE,
)
EXAMPLE_CODE = textwrap.dedent(
"""\
use qdrant_client::Qdrant;
pub async fn main() -> anyhow::Result<()> {
// Your code here
Ok(())
}
"""
)
assert RE_CODE.match(EXAMPLE_CODE) is not None
@classmethod
def shorten(cls, contents: str) -> str:
if (m := LanguageRust.RE_CODE.match(contents)) is None:
msg = "Invalid snippet format"
raise ValueError(msg)
return generic_shorten(
m["uses"].strip() + "\n\n" + textwrap.dedent(m["body"]).strip()
)
RE_RENDERED = re.compile(
r"""
(?P<uses> (?:use\s[^;]+;\n|\n)* )
(?P<body> .* )
$
""",
re.DOTALL | re.VERBOSE,
)
@classmethod
def unshorten(cls, contents: str) -> str:
if m := LanguageRust.RE_RENDERED.match(contents):
return textwrap.dedent(
"""\
{uses}
pub async fn main() -> anyhow::Result<()> {{
{body}
Ok(())
}}
"""
).format(
uses=m["uses"].strip(),
body=textwrap.indent(m["body"].strip(), " " * 4),
)
return contents
@@ -0,0 +1,53 @@
import json
import shutil
import subprocess
from pathlib import Path
from .base import CompileResult, Language, copy_template, trim_commonpath
class LanguageTypescript(Language):
NAME = "typescript"
SNIPPET_FILENAME = "typescript.ts"
@classmethod
def compile(cls, tmpdir: Path, fnames: list[Path]) -> CompileResult:
tmpdir.mkdir()
package_data = json.loads(Path("templates/typescript/package.json").read_text())
package_data["dependencies"]["@qdrant/js-client-rest"] = "file:" + str(
Path("clients/typescript/packages/js-client-rest").resolve().absolute()
)
(tmpdir / "package.json").write_text(json.dumps(package_data, indent=2))
shutil.copyfile(
"templates/typescript/package-lock.json", f"{tmpdir}/package-lock.json"
)
trimmed_fnames = trim_commonpath(fnames)
result = CompileResult()
for snippet_fname, trimmed_fname in trimmed_fnames.items():
dest_path = tmpdir / "s" / trimmed_fname
copy_template(snippet_fname, dest_path)
result.run_args[snippet_fname] = ["node", dest_path.with_suffix(".js")]
p = subprocess.run(["npm", "install"], cwd=tmpdir)
if p.returncode != 0:
result.has_issues = True
return result
p = subprocess.run(
["node_modules/.bin/tsc"]
+ ["s" / trimmed_fname for trimmed_fname in trimmed_fnames.values()],
cwd=tmpdir,
)
result.has_issues = p.returncode != 0
return result
@classmethod
def format(cls, fnames: list[str]) -> None:
# subprocess.run(["npx", "prettier", "--write", *fnames], check=True)
pass
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""Migrate the specified `.md` snippet to the runnable snippet file.
E.g., converts
`snippets/create-collection/simple/python.md`
to
`snippets/create-collection/simple/python.py`
Not reliable, might require manual adjustments.
Intended to be manually used to migrate non-runnable snippets into runnable
ones.
"""
import argparse
from pathlib import Path
from lib import log
from lib.languages import ALL_LANGUAGES
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("MD_FILE", nargs="+", type=Path)
args = parser.parse_args()
for md_file in args.MD_FILE:
if md_file.suffix != ".md":
log(f"Skipping non-markdown file: {md_file}")
continue
for lang in ALL_LANGUAGES:
if md_file.name == f"{lang.NAME}.md":
break
else:
log(f"Skipping unknown language file: {md_file}")
continue
lines = md_file.read_text().rstrip().splitlines()
if not lines[0].startswith("```") or lines[-1] != "```":
continue
content = "\n".join(lines[1:-1]) + "\n"
content = lang.unshorten(content)
log(f"Converting {md_file} to {lang.SNIPPET_FILENAME}")
(md_file.parent / lang.SNIPPET_FILENAME).write_text(content)
if __name__ == "__main__":
main()
+12
View File
@@ -0,0 +1,12 @@
[project]
name = "snippet-checker"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"requests>=2.32.5",
]
[dependency-groups]
dev = [
"mypy>=1.18.2",
]
+26
View File
@@ -0,0 +1,26 @@
with import <nixpkgs> { };
mkShell {
buildInputs = [
# for ./sync-clients.py
git
# for csharp client
dotnetCorePackages.sdk_8_0-bin
# for go client
go
# for java client
openjdk
# for python client
uv
# for rust client
cargo
rustfmt
# for typescript client
pnpm
];
}
+1
View File
@@ -0,0 +1 @@
../../qdrant-landing/content/documentation/headless/snippets
+202
View File
@@ -0,0 +1,202 @@
#!/usr/bin/env python3
import argparse
import re
import subprocess
import sys
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
# Change these to use different revisions by default/on CI
CSHARP_CLIENT_REVISION = "main"
JAVA_CLIENT_REVISION = "master"
TYPESCRIPT_CLIENT_REVISION = "master"
def main() -> None:
parser = argparse.ArgumentParser(
description="Fetch and prepare Qdrant client libraries."
)
subparsers = parser.add_subparsers(dest="command", required=True)
fetch_parser = subparsers.add_parser(
"fetch",
help="""Fetch client libraries and put them in the clients/ directory.
This is for java and C# clients.""",
)
fetch_parser.add_argument(
"--csharp",
help="Fetch and build the C# client.",
nargs="?",
const=CSHARP_CLIENT_REVISION,
metavar="GIT_REV",
)
fetch_parser.add_argument(
"--java",
help="Fetch the Java client.",
nargs="?",
const=JAVA_CLIENT_REVISION,
metavar="GIT_REV",
)
fetch_parser.add_argument(
"--typescript",
help="Fetch the TypeScript client.",
nargs="?",
const=TYPESCRIPT_CLIENT_REVISION,
metavar="GIT_REV",
)
lock_parser = subparsers.add_parser(
"lock",
help="Update lockfiles for templates/ directories.",
)
lock_parser.add_argument(
"--go",
help="Set Go client version",
nargs="?",
const="master",
metavar="GIT_REV",
)
lock_parser.add_argument(
"--rust",
help="""Set Rust client version.
Format: branch=BRANCH_NAME or tag=TAG_NAME or rev=COMMIT_HASH""",
nargs="?",
const="branch=master",
metavar="KIND=REV",
)
lock_parser.add_argument(
"--python",
help="""Set Python client version.
Format: branch=BRANCH_NAME or tag=TAG_NAME or rev=COMMIT_HASH""",
nargs="?",
const="branch=master",
metavar="KIND=REV",
)
args = parser.parse_args()
ok = False
if args.command == "fetch":
if args.csharp is not None:
url = "https://github.com/qdrant/qdrant-dotnet"
dir = BASE_DIR / "clients" / "csharp"
checkout_repo(url, dir, args.csharp)
prepare_csharp(dir)
ok = True
if args.java is not None:
url = "https://github.com/qdrant/java-client"
dir = BASE_DIR / "clients" / "java"
checkout_repo(url, dir, "master")
ok = True
if args.typescript is not None:
url = "https://github.com/qdrant/qdrant-js"
dir = BASE_DIR / "clients" / "typescript"
checkout_repo(url, dir, args.typescript)
prepare_typescript(dir)
ok = True
if not ok:
print("Specify at least one of --csharp, --java, --typescript to fetch.")
elif args.command == "lock":
if args.go is not None:
lock_go(args.go)
ok = True
if args.rust is not None:
lock_rust(args.rust)
ok = True
if args.python is not None:
lock_python(args.python)
ok = True
if not ok:
print("Specify at least one of --go, --rust to lock.")
def prepare_csharp(dir: Path) -> None:
subprocess.run(["git", "-C", dir, "clean", "-fxd"], check=True)
subprocess.run(
["dotnet", "run", "--project", "build", "--", "download-protos", "restore"],
check=True,
cwd=dir,
)
subprocess.run(
["dotnet", "build", "-c", "Release", "--nologo"],
check=True,
cwd=dir,
)
def prepare_typescript(dir: Path) -> None:
subprocess.run(["pnpm", "install"], cwd=dir, check=True)
subprocess.run(["pnpm", "-r", "build"], cwd=dir, check=True)
def lock_go(rev: str) -> None:
dir = BASE_DIR / "templates" / "go"
subprocess.run(
["go", "get", f"github.com/qdrant/go-client@{rev}"], cwd=dir, check=True
)
subprocess.run(["go", "mod", "tidy"], cwd=dir, check=True)
def lock_rust(spec: str) -> None:
dir = BASE_DIR / "templates" / "rust"
update_toml_spec(dir / "Cargo.toml", spec)
subprocess.run(["cargo", "update", "-p", "qdrant-client"], cwd=dir, check=True)
def lock_python(spec: str) -> None:
dir = BASE_DIR / "templates" / "python"
update_toml_spec(dir / "pyproject.toml", spec)
subprocess.run(["uv", "lock"], cwd=dir, check=True)
def update_toml_spec(path: Path, spec: str) -> None:
try:
kind, rev = spec.split("=", 1)
if kind not in ("branch", "tag", "rev"):
raise ValueError
except ValueError:
print("Invalid format for --rust/--python.")
print("Use branch=BRANCH_NAME or tag=TAG_NAME or rev=COMMIT_HASH.")
sys.exit(1)
toml_content = path.read_text().splitlines()
re_line = re.compile(r'^(qdrant-client = \{ git = "[^"]+").*$')
idx, m = next(
(i, m)
for i, line in enumerate(toml_content)
if (m := re_line.match(line)) is not None
)
toml_content[idx] = f'{m[1]}, {kind} = "{rev}" }}'
path.write_text("\n".join(toml_content) + "\n")
def checkout_repo(
repo_url: str,
dest_dir: Path,
rev: str,
) -> None:
if not dest_dir.exists():
print(f"Cloning repository from {repo_url} to {dest_dir}...")
subprocess.run(["git", "clone", repo_url, dest_dir], check=True)
else:
print(f"Repository already exists at {dest_dir}. Fetching latest changes...")
subprocess.run(["git", "-C", dest_dir, "fetch", "-p", "origin"], check=True)
# Try remote branch first (ignore local branches)
# If this check fails, it's either a commit hash, local branch, or tag
p = subprocess.run(
["git", "-C", dest_dir, "rev-parse", "--verify", f"refs/remotes/origin/{rev}"],
stdout=subprocess.PIPE,
text=True,
)
if p.returncode == 0:
rev = p.stdout.strip()
subprocess.run(["git", "-C", dest_dir, "checkout", "--detach", rev], check=True)
if __name__ == "__main__":
main()
@@ -0,0 +1,2 @@
/bin
/obj
@@ -0,0 +1,22 @@
try
{
foreach (var arg in args)
{
switch (arg)
{
// %cases%
default:
Console.WriteLine($"Unknown argument: {arg}");
Environment.Exit(1);
break;
}
}
}
catch (Exception e)
{
Console.Error.WriteLine($"Error occurred: {e.Message}");
Console.Error.WriteLine(e.StackTrace);
Environment.Exit(1);
}
await Task.CompletedTask;
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>snippets-amalgamation</AssemblyName>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../clients/csharp/src/Qdrant.Client/Qdrant.Client.csproj" />
</ItemGroup>
</Project>
+14
View File
@@ -0,0 +1,14 @@
module example.com/snippets-amalgamation
go 1.25.2
require github.com/qdrant/go-client v1.16.0
require (
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect
google.golang.org/grpc v1.76.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
)
+38
View File
@@ -0,0 +1,38 @@
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/qdrant/go-client v1.16.0 h1:DTkC3eppWXKhjQs+IgA9LFKOkSjJ4sTHs2jJPCni7dY=
github.com/qdrant/go-client v1.16.0/go.mod h1:I+EL3h4HRoRTeHtbfOd/4kDXwCukZfkd41j/9wryGkw=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+19
View File
@@ -0,0 +1,19 @@
package main
import (
"fmt"
_ "github.com/qdrant/go-client/qdrant"
"os"
// %imports%
)
func main() {
for _, arg := range os.Args[1:] {
switch arg {
// %cases%
default:
fmt.Printf("Unknown argument: %s\n", arg)
os.Exit(1)
}
}
}
@@ -0,0 +1 @@
/build
@@ -0,0 +1,51 @@
plugins {
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
group = 'com.example.snippets_amalgamation'
version = '1.0.0'
repositories {
mavenCentral()
}
dependencies {
implementation 'io.qdrant:client'
// gRPC dependencies - use the same version as Qdrant client
implementation 'io.grpc:grpc-netty-shaded:1.65.1'
implementation 'io.grpc:grpc-protobuf:1.65.1'
implementation 'io.grpc:grpc-stub:1.65.1'
implementation 'io.grpc:grpc-services:1.65.1'
// Protobuf runtime
implementation 'com.google.protobuf:protobuf-java:3.25.4'
// Logging
implementation 'org.slf4j:slf4j-simple:2.0.14'
}
application {
mainClass = 'com.example.snippets_amalgamation.Main'
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
// Create a proper fat jar and merge service descriptor files (required by gRPC)
shadowJar {
mergeServiceFiles()
archiveBaseName.set('app')
archiveClassifier.set('all')
}
// Keep the plain jar minimal (no dependency unpacking)
jar {
manifest {
attributes 'Main-Class': 'com.example.snippets_amalgamation.Main'
}
}
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+251
View File
@@ -0,0 +1,251 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+94
View File
@@ -0,0 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@@ -0,0 +1 @@
includeBuild(clientPath)
@@ -0,0 +1,19 @@
package com.example.snippets_amalgamation;
public class Main {
public static void main(String[] args) {
try {
for (String arg : args) {
switch (arg) {
// %cases%
default:
System.out.println("Unknown argument: " + arg);
}
}
} catch (Exception e) {
System.err.println("Error occurred: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
}
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env python3
from qdrant_client import QdrantClient
print(f"Hello, {QdrantClient}!")
@@ -0,0 +1,15 @@
[project]
name = "snippets-amalgamation"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"qdrant-client",
]
[tool.uv.sources]
qdrant-client = { git = "https://github.com/qdrant/qdrant-client", tag = "v1.16.0" }
[dependency-groups]
dev = [
"mypy>=1.18.2",
]
@@ -0,0 +1 @@
/target
@@ -0,0 +1,9 @@
[package]
name = "snippets-amalgamation"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.100"
qdrant-client = { git = "https://github.com/qdrant/rust-client", branch = "master" }
tokio = { version = "1.48.0", features = ["rt-multi-thread", "macros"] }
@@ -0,0 +1,14 @@
// %mods%
#[tokio::main]
async fn main() {
for arg in std::env::args().skip(1) {
match arg.as_str() {
// %cases%
_ => {
eprintln!("Unknown argument: {}", arg);
std::process::exit(1);
}
}
}
}
@@ -0,0 +1 @@
/node_modules
@@ -0,0 +1,17 @@
{
"name": "snippets-amalgamation",
"version": "1.0.0",
"description": "",
"main": "1.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"typescript": "^5.9.3"
},
"dependencies": {
"@qdrant/js-client-rest": "file:../../clients/typescript/packages/js-client-rest"
}
}