From 681c92eafd90af504b6cb4419a455775f61d7af2 Mon Sep 17 00:00:00 2001 From: Amadou Wolfgang Cisse Date: Sun, 1 Mar 2026 17:42:26 +0100 Subject: [PATCH 1/5] Adding ollama --- .github/workflows/cicd.yml | 1 + .skill-planning/ai-narrative-plan.md | 197 +++++++++++++++++ .skills/generate-documentation-intro.md | 196 +++++++++++++++++ .skills/generate-readme.md | 215 +++++++++++++++++++ .skills/targeted-marketing.md | 270 ++++++++++++++++++++++++ README.md | 1 + docker_db/__init__.py | 4 + docker_db/dbs/__init__.py | 3 + docker_db/dbs/ollama_db.py | 176 +++++++++++++++ docker_db/docker/model.py | 2 + tests/test_ollama.py | 102 +++++++++ usage/ollama_example.ipynb | 171 +++++++++++++++ usage/tmp/oldata/id_ed25519 | 7 + usage/tmp/oldata/id_ed25519.pub | 1 + 14 files changed, 1346 insertions(+) create mode 100644 .skill-planning/ai-narrative-plan.md create mode 100644 .skills/generate-documentation-intro.md create mode 100644 .skills/generate-readme.md create mode 100644 .skills/targeted-marketing.md create mode 100644 docker_db/dbs/ollama_db.py create mode 100644 tests/test_ollama.py create mode 100644 usage/ollama_example.ipynb create mode 100644 usage/tmp/oldata/id_ed25519 create mode 100644 usage/tmp/oldata/id_ed25519.pub diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index cb305f0..73d0773 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -23,6 +23,7 @@ jobs: - tests/test_mysql.py - tests/test_mongodb.py - tests/test_mssql.py + - tests/test_ollama.py steps: - name: Checkout diff --git a/.skill-planning/ai-narrative-plan.md b/.skill-planning/ai-narrative-plan.md new file mode 100644 index 0000000..8729b4d --- /dev/null +++ b/.skill-planning/ai-narrative-plan.md @@ -0,0 +1,197 @@ +# py-dockerdb — AI Narrative Arc Plan + +> **Core thesis**: Every serious AI application needs infrastructure. That infrastructure is painful to set up. py-dockerdb makes it a one-liner — locally, reproducibly, in Python. + +--- + +## The Narrative Arc + +``` +[Foundation] [Vector Layer] [Knowledge Layer] [Inference Layer] +Postgres/MySQL →→ pgvector / Redis →→ Neo4j (GraphRAG) →→ Ollama / LocalAI +MongoDB/MSSQL Redis Stack Elasticsearch ↑ + ↓ [Streaming Layer] + [Observability] Kafka / Redis Streams + TimescaleDB / InfluxDB +``` + +The library becomes the answer to: *"I just want to experiment with [hot AI pattern] without fighting Docker"* + +--- + +## Phase 0 — Ship What's In-Flight (feat_redis) + +**Status**: Branch exists, most code written. Needs polish + story. + +| Task | Output | +|---|---| +| Finalize `RedisDB` / `RedisConfig` | Merged, tested | +| Complete `001_pgvector_rag_example.ipynb` | Runnable end-to-end | +| Write `redis_example.ipynb` | Cache, pub/sub, streams basics | +| Update README with Redis + pgvector sections | Discoverability | +| PR → main | Release 0.8.0 | + +--- + +## Phase 1 — Complete the Vector Story + +### 1A. Redis Stack (Vector Search + Semantic Cache) + +**Why**: The plain `redis:7` image doesn't ship with RedisSearch or RedisVL. The Stack image does, and it's what LangChain's `RedisSemanticCache` and `redisvl` actually need. + +**What to build**: +- `RedisStackDB` + `RedisStackConfig` (image: `redis/redis-stack:latest`) +- Expose `RedisVL` client via `.connection` +- Notebook: **Semantic LLM response cache** — embed query → check Redis → skip OpenAI call on hit → show cost savings + +**Why it's hot**: LLM API cost reduction is a top enterprise pain point right now. + +### 1B. Elasticsearch / OpenSearch + +**Why**: Hybrid search (BM25 keyword + kNN vector) consistently beats pure vector search in production RAG benchmarks. ES is the most deployed search backend. + +**What to build**: +- `ElasticsearchDB` + `ElasticsearchConfig` (image: `elasticsearch:8`) +- `OpenSearchDB` variant (image: `opensearchproject/opensearch:2`) +- Notebook: **Hybrid RAG** — index docs with both dense vectors and BM25, use Reciprocal Rank Fusion, compare vs pgvector-only + +**Why it's hot**: "Hybrid search" is one of the top RAG improvement techniques of 2024–2025. + +--- + +## Phase 2 — Knowledge Graph Layer (GraphRAG) + +### Neo4j + +**Why**: Microsoft Research's GraphRAG paper (2024) triggered massive interest in graph-based retrieval. Neo4j is the dominant graph database with a production Docker image. LlamaIndex and LangChain both have Neo4j integrations. + +**What to build**: +- `Neo4jDB` + `Neo4jConfig` (image: `neo4j:5`, default bolt port 7687 + HTTP 7474) +- Expose `neo4j.GraphDatabase.driver()` via `.connection` +- Notebook: **GraphRAG pipeline** — extract entity relationships from documents → store as graph → traverse graph for context-enriched retrieval → compare answer quality vs naive RAG + +**Why it's hot**: GraphRAG is the current frontier of RAG research. Having a local Neo4j in one call is the exact pain point developers complain about. + +### Apache AGE (Bonus — Postgres Graph Extension) + +- Similar Dockerfile pattern to pgvector +- Enables graph queries inside Postgres — reduces infra footprint for smaller projects +- Lower priority than Neo4j but tells a good "Postgres does everything" story + +--- + +## Phase 3 — LLM Inference Layer + +### Ollama + +**Why**: This is the wildcard that could 10x the library's audience. Ollama is the de facto standard for running local LLMs (Llama 3, Mistral, Phi, Gemma). It ships as a Docker image. Managing it the same way you manage a database — `OllamaManager.create_db()`, `manager.connection` returns an OpenAI-compatible client — is a genuinely novel idea. + +**What to build**: +- `OllamaManager` + `OllamaConfig` (image: `ollama/ollama:latest`) +- `.pull_model(model_name)` method (calls Ollama pull API) +- `.connection` returns an `openai.OpenAI(base_url="http://localhost:11434/v1")` client +- Notebook: **Full local AI stack** — `OllamaManager` + `pgvector PostgresDB` + `Neo4jDB` = complete RAG system, zero cloud, zero cost + +**Why it's hot**: "Run AI locally" is the top developer interest category right now. This would make the library's story: *"The entire AI infrastructure stack, locally, in Python."* + +### LocalAI (Alternative/Complement) + +- OpenAI-compatible API, broader model format support (GGUF, GPTQ) +- Docker image: `localai/localai:latest` +- Lower priority than Ollama but worth noting as a variant + +--- + +## Phase 4 — Streaming & Async Pipelines + +### Apache Kafka + +**Why**: AI agents that call tools, process results, and chain actions are inherently async. Kafka is the standard message backbone for production AI pipelines. LangChain and LlamaIndex both have Kafka integrations. + +**What to build**: +- `KafkaManager` + `KafkaConfig` (image: `apache/kafka:3`) +- Note: Kafka needs ZooKeeper or KRaft mode — use KRaft (built-in since Kafka 3.3) to keep it single-container +- `.connection` returns `confluent_kafka.Producer` / `Consumer` +- Notebook: **Async AI agent pipeline** — tasks flow through Kafka topics → multiple agent workers consume → results written to Postgres + +**Why it's hot**: Agentic AI systems need reliable task queues. This is the infrastructure glue nobody wants to configure manually. + +--- + +## Phase 5 — Observability Layer + +### TimescaleDB + +**Why**: Monitoring LLM latency, token costs, embedding drift, and eval scores over time is a time-series problem. TimescaleDB is Postgres + time-series — same Dockerfile pattern as pgvector. + +**What to build**: +- `TimescaleDB` variant of `PostgresDB` (image: `timescale/timescaledb:latest-pg16`) +- Notebook: **LLM observability dashboard** — log every LLM call (latency, tokens, cost) to TimescaleDB → query rolling averages → detect regressions + +**Why it's hot**: LLMOps/MLOps observability is a top enterprise priority. + +### InfluxDB (Alternative) + +- Purpose-built time series, less SQL-familiar but simpler for pure metrics +- Image: `influxdb:2` +- Useful if Timescale feels too Postgres-heavy + +--- + +## Priority Ranking (Impact vs Effort) + +| Priority | Addition | Effort | AI Narrative Fit | Audience | +|---|---|---|---|---| +| 🔴 Now | pgvector RAG notebook polish | Low | Vector RAG | All AI devs | +| 🔴 Now | Redis Stack semantic cache | Medium | Cost reduction | Enterprise | +| 🟠 Next | Neo4j + GraphRAG notebook | Medium-High | GraphRAG frontier | Research-aware devs | +| 🟠 Next | Elasticsearch hybrid search | Medium | Production RAG | ML engineers | +| 🟡 Soon | Ollama manager | High | Local AI stack | Indie/hobbyist devs | +| 🟡 Soon | TimescaleDB observability | Medium | LLMOps | MLOps engineers | +| 🟢 Later | Kafka async pipelines | High | Agentic AI | Platform engineers | +| 🟢 Later | Apache AGE | Low | Postgres story | Postgres fans | + +--- + +## Content & Discoverability Plan + +The library needs a **notebook series** that tells the story end-to-end: + +| Notebook | Concept | Tags | +|---|---|---| +| `001_pgvector_rag_example.ipynb` | Basic vector RAG | rag, pgvector, llamaindex | +| `002_redis_semantic_cache.ipynb` | LLM semantic caching | redis, langchain, cost | +| `003_hybrid_search_rag.ipynb` | BM25 + vector hybrid | elasticsearch, rag, search | +| `004_graphrag_neo4j.ipynb` | GraphRAG pipeline | neo4j, graphrag, knowledge-graph | +| `005_local_ai_stack.ipynb` | Full local stack | ollama, pgvector, neo4j, local-llm | +| `006_llmops_observability.ipynb` | Monitoring LLM calls | timescaledb, mlops, metrics | +| `007_agent_pipeline_kafka.ipynb` | Async agent pipeline | kafka, agents, async | + +Each notebook = a standalone shareable artifact (HuggingFace, Medium, Reddit r/LocalLLaMA). + +--- + +## Positioning + +**Tagline candidates**: +- *"The entire local AI infrastructure stack, in one `pip install`"* +- *"Stop fighting Docker. Start building AI."* +- *"pgvector, Neo4j, Redis, Ollama — one Pythonic API"* + +**Comparison table for README**: + +| Tool | What you'd normally do | With py-dockerdb | +|---|---|---| +| pgvector | Write Dockerfile, init SQL, manage container | `PostgresDB(PostgresConfig(dockerfile_path="Dockerfile.pgvector")).create_db()` | +| Neo4j | Pull image, set env vars, wait for bolt port | `Neo4jDB(Neo4jConfig()).create_db()` | +| Ollama | Install Ollama, manage process, remember flags | `OllamaManager(OllamaConfig(model="llama3")).create_db()` | +| Redis Stack | Pull stack image, configure modules | `RedisStackDB(RedisStackConfig()).create_db()` | + +--- + +## Open Questions + +1. **Scope boundary**: Does Ollama fit the "database management" framing, or should it live in a separate but related library? (Suggestion: keep it in, rename the library conceptually to "AI infrastructure manager") +2. **Docker Compose support**: Some stacks (Kafka + ZooKeeper, Weaviate + its deps) are multi-container. Worth adding a `ComposeManager` base class? +3. **GPU containers**: Ollama on GPU requires `--gpus all` flag — is that in scope? +4. **Hosted vector DBs**: Should there be a `QdrantDB` (purpose-built vector DB, single Docker container, excellent Python SDK) — probably yes, lower effort than Neo4j. diff --git a/.skills/generate-documentation-intro.md b/.skills/generate-documentation-intro.md new file mode 100644 index 0000000..b067be2 --- /dev/null +++ b/.skills/generate-documentation-intro.md @@ -0,0 +1,196 @@ +--- +name: sphinx-intro-docs +description: > + Generates the introductory Sphinx documentation section for a Python repository: + a landing page and a quickstart guide. Anchors both files to the confirmed audience + and framing from a marketing prose output, and derives quickstart examples directly + from usage notebooks rather than inventing them. + Use this skill whenever the user wants to create or improve the introductory part of + their Sphinx docs, set up a docs landing page, write a getting-started guide, or + produce the human-readable entry point to an API reference site. Trigger even if the + user phrases it as "write the intro for my docs", "make a quickstart page", or + "set up the first page of my documentation". +--- + +# Sphinx Intro Docs + +Generates two files that form the introductory layer of a Sphinx documentation site: +a landing page and a quickstart guide. Both are derived from existing project artefacts +rather than written from scratch, ensuring the docs stay consistent with the README and +marketing positioning. + +--- + +## Inputs to gather + +Read these before writing anything. They are listed in priority order. + +| Input | Where to find it | Required | +|---|---|---| +| Marketing prose output | `repo_marketing_prose.md` in project root or outputs dir | Yes | +| README.md | Project root | Yes | +| Usage notebooks | `/usage/*.ipynb` | Strongly preferred | +| Existing Sphinx conf.py | `docs/conf.py` | Preferred | +| Existing toctree structure | `docs/index.rst` or `docs/introduction.rst` | If present | + +If no marketing prose file is found, ask the user to run the `repo-marketing-prose` +skill first. The landing page without confirmed positioning will default to generic copy +that serves no audience. + +If no usage notebooks are found, fall back to the README usage section, but flag this +to the user — the quickstart will be weaker without runnable notebook examples. + +--- + +## What to extract from each input + +**From marketing prose:** +- Confirmed target audience and any secondary audience +- Keyword list +- Hero section (use verbatim on the landing page) +- Elevator pitch for each audience card — these inform the "Who is this for" section +- "Where they gather" from each card — useful context for tone but not written into docs + +**From README.md:** +- The single canonical usage example — reuse it in the quickstart rather than + rewriting it. Keep the code identical; only adjust surrounding prose if needed. +- The "When to use this" one-liners — adapt (do not copy verbatim) for the landing page +- Prerequisites and installation steps — copy these exactly into the quickstart + +**From usage notebooks:** +- Read markdown cells to understand the narrative arc of each notebook +- Extract the first complete workflow from the most beginner-friendly notebook + (prefer a notebook named `quickstart`, `intro`, or `example` over engine-specific ones) +- Note any "what next" or "see also" references at the end of notebooks — use these + to populate the "Next steps" section of the quickstart + +--- + +## Phase 1: Landing Page + +### File +Produce `docs/introduction.rst` (or `.md` if the project uses MyST — check `conf.py` +for `myst_parser` in extensions). + +### Structure + +``` +[Hero section from marketing prose — verbatim] + +Who is this for +--------------- +Short paragraph per confirmed audience (primary first, secondary second). +Draw from the audience card elevator pitches. 2-3 sentences each. +Do not list features here — this section answers "is this for me?". + +What you can do with {project name} +------------------------------------ +Adapt the "When to use this" one-liners from the README into a slightly fuller +form — one short paragraph per use case, not a bullet list. +Each paragraph should name the context, the action, and the payoff. +Use keywords from the marketing prose naturally. + +Where to go next +---------------- +A toctree or a short set of links: +- Quickstart (link to quickstart page) +- API Reference (link to modules page if it exists) +- Usage notebooks (link to /usage directory or rendered notebook pages) +Keep this section minimal — it is navigation, not content. +``` + +### Tone +Match the confirmed audience tone from the marketing prose. The landing page sits +between the README (persuasive) and the quickstart (instructional) — it should feel +welcoming and orienting, not salesy and not yet technical. + +--- + +## Phase 2: Quickstart Guide + +### File +Produce `docs/quick_start.md` (MyST) or `docs/quick_start.rst` depending on project +setup. Check `conf.py` — if `myst_parser` is in extensions, use `.md`. + +### Structure + +``` +# Quickstart + +One sentence: what the reader will have working by the end of this page. + +## Prerequisites +[Copy exactly from README prerequisites section] + +## Installation +[Copy exactly from README installation section] + +## Your first {project name} workflow +[Single canonical example from README, identical code] +Brief prose before the code block explaining what is about to happen. +Brief prose after explaining what just ran and what the output means. + +## What just happened +2-4 sentences explaining the pattern the example demonstrated — config, create, +connect, teardown in the case of py-dockerdb. This is the one place where a +core concept is introduced, but only the concept directly visible in the example. +Do not introduce abstractions that are not in the code block. + +## Next steps +- Link to each usage notebook by name with a one-line description of what it covers +- Link to the API reference if it exists +- Link to any configuration or debug guides if they exist +``` + +### On the code example +Use the identical code from the README canonical example. Do not simplify, shorten, +or abstract it — a quickstart that runs is more valuable than one that is tidy. +If the example requires a teardown step, include it and note why (leaked containers, +cleanup, reproducibility — whatever is relevant to the confirmed audience). + +### Tone +Instructional and direct. The reader has committed to trying the tool. Minimize +explanation before the first code block — they want to run something, not read. +Save explanation for after the code has run. + +--- + +## Output files + +Save both files to the `docs/` directory (or wherever the existing Sphinx source +files live — check `conf.py` for `source_dir` if present). + +After generating both files, update the toctree immediately so the pages appear in +sidebar navigation on the next build. Do not leave this to the user. + +Find the relevant toctree file — typically `docs/index.rst` or `docs/introduction.rst`. +If an `introduction.rst` toctree exists, add entries there. If only `index.rst` exists, +add them there. If neither exists, create a minimal `introduction.rst` that wraps both +files and add a reference to it in `index.rst`. + +Add entries in this order: landing page first, quickstart second: + +```rst +.. toctree:: + :maxdepth: 2 + + introduction + quick_start +``` + +If the toctree already has entries, insert the new ones at the top of the relevant +toctree block so the intro section leads the navigation. + +After updating, tell the user exactly which file was modified and what was added. + +--- + +## Writing style + +- No em dashes +- Active voice +- No marketing superlatives ("powerful", "robust", "seamless") +- RST: use underline-only headings (`===`, `---`, `~~~`) consistent with existing files +- MyST: use standard `#` heading hierarchy +- Code blocks must specify language (` ```python `, ` ```bash `, etc.) +- Cross-references to other doc pages use `:doc:` in RST or relative links in MyST \ No newline at end of file diff --git a/.skills/generate-readme.md b/.skills/generate-readme.md new file mode 100644 index 0000000..b240b43 --- /dev/null +++ b/.skills/generate-readme.md @@ -0,0 +1,215 @@ +--- +name: readme-generator +description: Generates comprehensive README.md files for software projects by analyzing codebase structure +allowed-tools: ["Read", "Glob", "Grep", "Write"] +version: 1.1.0 +author: GLINCKER Team +license: Apache-2.0 +keywords: [documentation, readme, markdown, project] +--- + +# README Generator + +Automatically generates professional, comprehensive README.md files by analyzing your +project structure, dependencies, usage examples, and marketing prose. + +## What This Skill Does + +This skill helps you create high-quality README files by: +- Anchoring the framing and tone to a confirmed marketing prose output throughout the + entire document, not just the opening +- Propagating the confirmed audience, keywords, and all audience cards into the structure + and content of every section +- Extracting real usage patterns from notebooks in the `/usage` directory +- Analyzing project structure and identifying key components +- Detecting programming languages and frameworks +- Generating appropriate sections with relevant content +- Following README best practices + +--- + +## Instructions + +### 0. Priority Inputs (read these first, before anything else) + +**Marketing prose output** — look for `repo_marketing_prose.md` in the project root or +outputs directory. If found, extract and hold all of the following in mind for the +entire README, not just the opening section: + +- **Confirmed target audience** — every section should be written for this person. + Their vocabulary, their pain points, their level of familiarity with the tooling. +- **Keyword list** — these terms must appear naturally across the README body: in + section headings where relevant, in the first sentence of the Usage section, in any + "When to use this" or workflow descriptions. Do not cluster them in one place. +- **README Hero Section** — use verbatim as the opening. Do not rewrite or paraphrase. +- **Audience Cards (all of them, including unchosen ones)** — read the elevator pitch + and pain point of each card. Use the primary audience card to drive the "When to use + this" section. Use the secondary audience card (if one was confirmed) to add a + secondary workflow. Use any remaining cards to identify use cases worth naming even + if they are not the lead framing — for example, a RAG audience card means "RAG + prototype" belongs in the usage examples and keyword appearances even if it is not + the primary audience. +- **Social share blurb** — the core problem statement here should be echoed in the + introduction paragraph that immediately follows the hero. + +If no marketing prose file is found, ask the user before proceeding. Generating a README +without confirmed positioning produces generic copy that serves no audience well. + +**Usage notebooks** — check the `/usage` directory for `.ipynb` files. For each notebook: +- Read the markdown cells for intent and narrative +- Extract code cells as usage examples, preserving the actual API calls +- Note which engines, configurations, or workflows are demonstrated +- Prefer notebook examples over invented ones in the Usage section +- Check each example for completeness: does it show setup, use, AND teardown? If + teardown (stop, delete, cleanup) is present in the notebook, include it. If it is + missing from the example but present in the codebase, add it with a note. + +--- + +### 1. Project Discovery + +After reading the priority inputs, analyze the project structure: +- Use Glob to find key files: `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod` +- Use Glob to identify main source directories +- Use Read to examine configuration files +- Use Grep to find test files and CI configuration + +### 2. Content Analysis + +Based on findings, determine: +- Project type (library, application, CLI tool, etc.) +- Primary programming language(s) +- Dependencies and frameworks +- Build and test commands +- License type (from LICENSE file) + +--- + +### 3. README Structure + +Build the README in this order. Sections marked **required** must always appear. +Sections marked **audience-driven** must appear if the marketing prose supports them. +Sections marked **optional** appear only if relevant project files exist. + +#### [required] Hero +Use the hero section from marketing prose verbatim. Do not alter it. + +#### [required] Introduction paragraph +One short paragraph (3-5 sentences) drawn from the social share blurb and the primary +audience card's pain point. This is the narrative bridge between the hero and the +technical content. It should answer "is this for me?" for the confirmed audience. + +#### [audience-driven] When to use this +A short section with 3-5 one-liners, one per audience card (primary, secondary, and +any remaining cards that represent valid use cases). Each line names the context and +the payoff. Example format: + +- **Teaching a SQL workshop** — seed classroom datasets automatically and skip the + environment setup section entirely. +- **Comparing databases for an MVP** — switch engines without changing connection code. +- **Local RAG prototype** — spin up a backing store, test it, swap it out in one + config change. + +Order by: primary audience first, secondary audience second, remaining audiences last. +Use the keywords from the marketing prose naturally in these lines. + +#### [required] Prerequisites +Software and system requirements. + +#### [required] Installation + +#### [required] Usage +Lead the section with one sentence that names the confirmed audience and the primary +use case, using keywords from the marketing prose. + +Then present a single canonical example drawn from the primary audience's most likely +entry point (for an instructor audience, use PostgreSQL; for an MVP audience, use +whichever engine is most prominent in the notebooks). The example must show setup, +core usage, AND teardown as a complete lifecycle. + +After the single example, add a "More examples" subsection that links directly to each +notebook in the `/usage` directory by filename. Do not reproduce the other engine +examples inline — the notebooks are the right place for that detail. + +Surface any notably useful capabilities (init scripts, connection string helpers, +engine switching) as named subsections with a brief description and a short snippet +only if the snippet is 5 lines or fewer. Order subsections by relevance to the +confirmed audience, not by implementation order. + +#### [optional] Development +How to set up for development. Include only if a dev setup process exists. + +#### [optional] Testing +How to run tests. Include only if a test suite exists. + +#### [optional] Contributing + +#### [required] License + +--- + +### 4. Writing Style + +- Clear, concise language +- Active voice +- No em dashes +- Code blocks with proper syntax highlighting +- Badge shields for status indicators (if CI/CD detected) +- No emoji unless user requests +- Tone must match the confirmed audience from marketing prose: + +| Audience type | Tone | +|---|---| +| Instructor / educator | Practical, time-saving framing, classroom-aware | +| Enterprise / Azure | Professional, precise, productivity-focused | +| ML / RAG builder | Technical, experiment-friendly, iteration-aware | +| MVP / early-stage builder | Direct, low-overhead, decision-reducing | + +--- + +### 5. Output + +Present the generated README to the user and offer to: +- Write it to README.md +- Make adjustments based on feedback +- Add additional sections + +--- + +## Configuration + +This skill adapts to project type: + +| Project Type | Key Files | Focus Areas | +|---|---|---| +| Python | `pyproject.toml`, `setup.py` | pip install, virtual env | +| Node.js | `package.json` | npm install, scripts | +| Rust | `Cargo.toml` | cargo build, features | +| Go | `go.mod` | go get, modules | +| Generic | None | Basic structure | + +## Tool Requirements + +- **Read**: Examine configuration, source files, marketing prose, and notebooks +- **Glob**: Find relevant files across project, including `/usage/*.ipynb` +- **Grep**: Search for patterns (tests, CI, etc.) +- **Write**: Create the README.md file + +## Limitations + +- Cannot include screenshots (user must add manually) +- May miss custom build processes not in standard files +- Generates a starting point - user should review and customize +- Works best with standard project structures +- Does not analyze actual code logic for features + +## Error Handling + +- **No marketing prose found**: Ask user to run the `repo-marketing-prose` skill first, + or confirm they want a generic README with no audience targeting +- **No usage notebooks found**: Fall back to docstring examples and quickstart docs, + note to user that `/usage` notebooks would improve the examples section +- **No project files found**: Ask user to confirm working directory +- **Multiple languages detected**: Generate sections for each, note polyglot nature +- **Existing README**: Prompt user before overwriting, offer to merge +- **Missing key info**: Generate placeholder sections with TODO markers \ No newline at end of file diff --git a/.skills/targeted-marketing.md b/.skills/targeted-marketing.md new file mode 100644 index 0000000..3bc86bc --- /dev/null +++ b/.skills/targeted-marketing.md @@ -0,0 +1,270 @@ +--- +name: repo-marketing-prose +description: > + Generates SEO-optimised marketing copy for open-source or internal Python repositories. + Produces a full suite of prose assets: a README hero section, a PyPI / package-index + long description, GitHub repository "About" one-liner, platform-specific social-share + blurbs, a keyword-rich meta description for documentation sites, GitHub topic tags, + and PyPI classifier blocks. + Use this skill whenever the user wants to make a repository more discoverable, attractive, + or professional-looking — even if they phrase it as "write a README", "make my repo look + good", "help me get more stars", "write a description for PyPI", or "how do I market my + open-source project". Trigger on requests that explicitly touch repo presentation, + discoverability, or developer audience growth. +--- + +# Repo Marketing Prose + +Generates a cohesive set of marketing and SEO copy for a software repository by reading +the existing project artefacts and synthesising them into prose that is accurate, +developer-friendly, and optimised for search. + +--- + +## Inputs to gather + +Before writing, collect as much of the following as is available. Read files directly +from the repository or ask the user to paste them. + +| Input | Where to find it | Required | +|---|---|---| +| Project name and tagline | `pyproject.toml`, `setup.py`, existing README | Yes | +| What the project does (one sentence) | README, docstrings, `__init__.py` | Yes | +| Primary user persona | README, docs motivation section | Strongly preferred | +| Key features (3-5 bullet points worth) | README, docs, changelog | Strongly preferred | +| Tech stack / dependencies | `pyproject.toml`, `requirements.txt` | Strongly preferred | +| Existing README | Filesystem | If present | +| Existing docs (motivation, quickstart) | Filesystem or uploaded files | If present | +| Example usage notebooks | `/usage` directory (`.ipynb` files) | Optional | +| Competitor or comparable tools | User input | Optional | +| Target keywords | User input | Optional | + +If the user has already shared files in the conversation (e.g. a motivation doc, quickstart, +conf.py), extract what you need from those first before asking. + +--- + +## Outputs to produce + +Never add any of these: — + +Always produce all seven assets in a single response, clearly separated by headings. +Save them to a single Markdown file so the user can copy-paste from one place. + +### 1. README Hero Section +The first ~20 lines of a README. Includes: +- H1 project name with a one-line tagline +- A `pip install {package-name}` code block immediately after the tagline +- A 2-3 sentence elevator pitch (what it is, who it is for, what makes it different) +- A shield/badge line (build, PyPI version, licence) — use placeholder URLs the user + can fill in +- A short "Why this project?" paragraph (3-5 sentences) that frames the problem, + positions the tool relative to alternatives, and names at least one comparable tool + by name if competitors were provided + +### 2. PyPI Long Description Intro +A 100-150 word prose block suitable for the top of a `pyproject.toml` description +or a PyPI project page. Must be plain prose (no headers, no bullet lists) because +PyPI renders the full README but search engines index only the first ~150 words. +Front-load the most important keywords naturally. + +### 3. GitHub "About" One-Liner +A single sentence of 120-160 characters for the repository's About field. +Must include: what the tool does, primary tech context (e.g. "for Azure Data Factory"), +and one differentiating adjective. No marketing superlatives ("best", "ultimate"). + +### 4. Social Share Blurbs +Three platform-specific variants. Each mentions the problem it solves and ends with +a call to action. + +- **Twitter/X**: Max 280 characters. Punchy, no hashtags unless they are the canonical + community tag (e.g. `#rstats`). Link at the end. +- **LinkedIn**: 3-4 sentences. Professional tone, can name-drop the tech stack. Ends + with a question or invitation to comment. +- **Hacker News "Show HN"**: One sentence title in `Show HN: {title}` format, followed + by 2-3 sentences of plain prose context. No hype. What it does, why you built it, + where to find it. + +### 5. Documentation Meta Description +A 150-160 character string for the `html_meta` or `description` field in Sphinx `conf.py` +or a `` tag. Must naturally include the project name and primary +use-case keyword. + +### 6. GitHub Topics +8-12 lowercase hyphenated tags for the repository's Topics field. Topics are the primary +GitHub browse/discovery mechanism. Include: language (`python`), infrastructure type, +primary use-case, and any framework integrations present in the codebase. +Format as a comma-separated list the user can paste directly into GitHub's Topics field. + +### 7. PyPI Classifiers +A ready-to-paste `classifiers = [...]` block for `pyproject.toml`. Include at minimum: +development status, intended audience, licence, programming language versions, and +topic classifiers that match the project's primary use-case. Use only valid +[PyPI trove classifiers](https://pypi.org/classifiers/). + +--- + +## SEO principles to apply + +- **Front-load keywords**: The most important search terms (project name, primary verb, + primary noun) belong in the first sentence of every asset, not buried mid-paragraph. +- **Use the words developers search for**: Prefer "deploy SQL pipelines to Azure Data Factory" + over "manage your data transformation workflows in the cloud." +- **Be specific over superlative**: "Generates one ADF pipeline per dbt model" ranks and + converts better than "powerful, flexible solution." +- **Mirror competitor vocabulary**: If users search for "dbt alternative" or "ADF pipeline + builder", those phrases should appear naturally in the copy. If competitors were provided, + name at least one in the README hero's "Why this project?" paragraph — developers + searching for alternatives will find the repo via those names. +- **Avoid keyword stuffing**: Each keyword cluster should appear 2-3 times across the full + asset set, not repeatedly in a single paragraph. +- **Active voice throughout**: "Deploy your models with one command" not "Models can be + deployed using a single command." + +--- + +## Tone guidelines + +The tone should match the project's apparent audience. Infer from the tech stack: + +| Stack signals | Tone | Emoji | +|---|---|---| +| Azure / enterprise tooling | Professional, precise, productivity-focused | Never | +| ML / research libraries | Technical, peer-to-peer, understated | Rarely, only in social blurbs | +| CLI / developer tools | Direct, terse, shows-don't-tells | Never | +| Data engineering | Pragmatic, problem-first, ops-aware | Never | +| AI / LLM developer tooling | Practical, local-first workflow, notebook-centric examples | Occasionally in social blurbs only | + +If an existing README is present, match its emoji style exactly — do not introduce or +remove emoji relative to what the author already uses. + +Never use: "cutting-edge", "revolutionary", "game-changing", "seamlessly", "leverage" +(as a verb), "robust solution", "best-in-class". + +--- + +## Workflow + +The workflow has two mandatory phases. **Do not generate artifacts until the user has +confirmed an audience at the end of Phase 1.** + +### Phase 1: Audience Discovery + +1. Read all available project files (README, motivation docs, quickstart, pyproject.toml, + conf.py, __init__.py). Note: the user may have already shared these in the conversation. +2. Check the `/usage` directory for example notebooks (`.ipynb` files). Scan their cell + sources for concrete usage patterns, API entry points, and real-world use cases — + these reveal what users actually do with the tool, not just what the author intended. +3. Identify 2-4 plausible audience segments. Think beyond the obvious intended user — + look for adjacent communities who share the same pain point but arrive from a different + background or toolchain. For example, a tool built for data engineers might be equally + compelling to analytics engineers, DevOps teams managing data infrastructure, or + backend developers new to the data space. +4. For each candidate audience, produce an **Audience Card**: + + ``` + ### Audience: {name} + Who they are: 1-2 sentences on their role, background, and daily context. + Their pain point: The specific problem this tool solves for them. + Why this framing works: What makes the tool compelling to this group specifically. + Where they gather: 2-4 concrete channels — subreddits, newsletters, Slack/Discord + communities, conferences, YouTube channels, LinkedIn groups, Hacker News tags, etc. + Elevator pitch: 2-3 sentences written in the voice and vocabulary of this audience. + Keywords they search for: 4-6 terms this group would actually type into Google or GitHub. + ``` + +5. Present all Audience Cards and ask the user to either: + - Pick one audience to target exclusively, and confirm or adjust its keyword list + - Pick a primary and secondary audience (copy will lead with one, not alienate the other), + and confirm or adjust the combined keyword list + - Propose a different audience not on the list + + Include the keyword list for each candidate audience in this message so the user can + adjust them in a single reply. Do not ask for keyword confirmation in a separate step. + +### Phase 2: Full Artifact Generation + +Only begin after the user has confirmed an audience and keywords in Phase 1. + +6. Write all seven assets anchored to the confirmed audience framing and keyword list. +7. Append all Phase 1 Audience Cards (including the ones not chosen) under an + "Audience Cards Considered" section at the bottom of the output file. +8. Save to `.skill-planning/marketing_prose.md` relative to the project root, and + present the file path to the user. + +--- + +## Output file format + +```markdown +# Marketing Prose: {Project Name} + +**Target audience:** {confirmed audience name} +**Keyword targets:** {comma-separated list} + +--- + +## 1. README Hero Section + +{content} + +--- + +## 2. PyPI Long Description Intro + +{content} + +--- + +## 3. GitHub "About" One-Liner + +{content} + +--- + +## 4. Social Share Blurbs + +### Twitter/X +{content} + +### LinkedIn +{content} + +### Hacker News "Show HN" +{content} + +--- + +## 5. Documentation Meta Description + +{content} + +--- + +## 6. GitHub Topics + +{comma-separated list} + +--- + +## 7. PyPI Classifiers + +```toml +classifiers = [ + {entries} +] +``` + +--- + +## Audience Cards Considered + +> These were the candidate audiences evaluated in Phase 1. Preserved here so you can +> revisit targeting without starting from scratch. + +### Audience: {name} +... + +### Audience: {name} +... +``` diff --git a/README.md b/README.md index b8f6fb5..3b10461 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ The following databases are supported: - Microsoft SQL Server - PostgreSQL - MySQL + - Ollama (local LLM runtime) These databases might be added in the future: - Cassandra diff --git a/docker_db/__init__.py b/docker_db/__init__.py index 62057d4..b1ef205 100644 --- a/docker_db/__init__.py +++ b/docker_db/__init__.py @@ -7,6 +7,8 @@ MSSQLDB, MySQLConfig, MySQLDB, + OllamaConfig, + OllamaDB, PostgresConfig, PostgresDB, ) @@ -21,6 +23,8 @@ "PostgresConfig", "MySQLDB", "MySQLConfig", + "OllamaDB", + "OllamaConfig", "MSSQLDB", "MSSQLConfig", ] diff --git a/docker_db/dbs/__init__.py b/docker_db/dbs/__init__.py index 4aed49b..f01340d 100644 --- a/docker_db/dbs/__init__.py +++ b/docker_db/dbs/__init__.py @@ -4,6 +4,7 @@ from docker_db.dbs.postgres_db import PostgresDB, PostgresConfig from docker_db.dbs.mysql_db import MySQLDB, MySQLConfig from docker_db.dbs.mssql_db import MSSQLDB, MSSQLConfig +from docker_db.dbs.ollama_db import OllamaDB, OllamaConfig __all__ = [ "MongoDB", @@ -14,4 +15,6 @@ "MySQLConfig", "MSSQLDB", "MSSQLConfig", + "OllamaDB", + "OllamaConfig", ] diff --git a/docker_db/dbs/ollama_db.py b/docker_db/dbs/ollama_db.py new file mode 100644 index 0000000..4cec93f --- /dev/null +++ b/docker_db/dbs/ollama_db.py @@ -0,0 +1,176 @@ +""" +Ollama container management module. + +This module provides classes to manage Ollama containers using Docker. +It enables developers to easily create, configure, start, stop, and delete Ollama +containers for development and testing purposes. +""" +import time + +import docker +import requests +from docker.models.containers import Container +from pydantic import Field +from requests.exceptions import RequestException + +from docker_db.docker import ContainerConfig, ContainerManager + + +class OllamaConfig(ContainerConfig): + """Configuration for an Ollama container.""" + database: str = Field( + default="ollama", + description="Compatibility field required by the base container manager API.", + ) + model: str | None = Field( + default=None, + description="Optional model name to pull after container startup.", + ) + pull_model_on_create: bool = Field( + default=False, + description="If True and model is set, pull the model in create_db().", + ) + port: int = Field(default=11434, description="Port on which Ollama listens") + env_vars: dict = Field( + default_factory=dict, + description="A dictionary of environment variables to set in the container", + ) + _type: str = "ollama" + + +class OllamaDB(ContainerManager): + """Manages lifecycle of an Ollama container via Docker SDK.""" + + def __init__(self, config: OllamaConfig): + self.config: OllamaConfig = config + assert self._is_docker_running() + self.client = docker.from_env() + + @property + def connection(self): + """ + Establish a new HTTP session to the Ollama API. + + Returns + ------- + requests.Session + A new HTTP session object. + """ + return requests.Session() + + @property + def base_url(self) -> str: + """Return Ollama API base URL.""" + return f"http://{self.config.host}:{self.config.port}" + + def connection_string(self, db_name: str = None, sql_magic: bool = False) -> str: + """Get Ollama base URL.""" + return self.base_url + + def _get_environment_vars(self): + default_env_vars = {} + default_env_vars.update(self.config.env_vars) + return default_env_vars + + def _get_volume_mounts(self): + return [ + docker.types.Mount( + target="/root/.ollama", + source=str(self.config.volume_path), + type="bind", + ) + ] + + def _get_port_mappings(self): + return {"11434/tcp": self.config.port} + + def _get_healthcheck(self): + return { + "Test": ["CMD", "ollama", "--version"], + "Interval": 5000000000, # 5s + "Timeout": 3000000000, # 3s + "Retries": 10, + } + + def _wait_for_api_ready(self) -> bool: + for _ in range(self.config.retries): + try: + response = requests.get( + f"{self.base_url}/api/tags", + timeout=5, + ) + if response.status_code == 200: + return True + except RequestException: + pass + time.sleep(self.config.delay) + return False + + def _create_db( + self, + db_name: str | None = None, + container: Container | None = None, + ): + """ + Validate Ollama API availability and optionally pull a model. + """ + container = container or self.client.containers.get(self.config.container_name) + container.reload() + if not container.attrs.get("State", {}).get("Running", False): + raise RuntimeError(f"Container {container.name} is not running.") + + if not self._wait_for_api_ready(): + raise RuntimeError("Ollama API did not become ready in time.") + + if self.config.pull_model_on_create and self.config.model: + self.pull_model(self.config.model) + + self.database_created = True + return container + + def _wait_for_db(self, container: Container | None = None) -> bool: + """ + Wait until Ollama API is accepting connections and ready. + """ + try: + container = container or self.client.containers.get(self.config.container_name) + for _ in range(self.config.retries): + container.reload() + state = container.attrs.get("State", {}) + if state.get("Running", False): + break + time.sleep(self.config.delay) + except (docker.errors.NotFound, docker.errors.APIError): + pass + + return self._wait_for_api_ready() + + def pull_model(self, model: str): + """ + Pull a model via Ollama API. + """ + response = requests.post( + f"{self.base_url}/api/pull", + json={"model": model, "stream": False}, + timeout=600, + ) + if response.status_code != 200: + raise RuntimeError(f"Failed to pull model '{model}': {response.text}") + return response.json() + + def list_models(self) -> list[dict]: + """ + List locally available models. + """ + response = requests.get(f"{self.base_url}/api/tags", timeout=10) + if response.status_code != 200: + raise RuntimeError(f"Failed to list models: {response.text}") + payload = response.json() + return payload.get("models", []) + + def test_connection(self): + """ + Ensure Ollama API is reachable. + """ + if not self._wait_for_api_ready(): + raise ConnectionError("Ollama API is not reachable.") diff --git a/docker_db/docker/model.py b/docker_db/docker/model.py index 8f12099..06083c7 100644 --- a/docker_db/docker/model.py +++ b/docker_db/docker/model.py @@ -12,6 +12,7 @@ "mssql": "ms", "mongodb": "mg", "cassandra": "cs", + "ollama": "ol", } DEFAULT_IMAGE_MAP = { @@ -21,6 +22,7 @@ "mssql": "mcr.microsoft.com/mssql/server:2022-latest", "mongodb": "mongo:6", "cassandra": "cassandra:4", + "ollama": "ollama/ollama:latest", } class ContainerConfig(BaseModel): diff --git a/tests/test_ollama.py b/tests/test_ollama.py new file mode 100644 index 0000000..4c9a719 --- /dev/null +++ b/tests/test_ollama.py @@ -0,0 +1,102 @@ +import time +import uuid +from pathlib import Path + +import docker +import pytest +import requests +from docker.models.containers import Container + +from docker_db.dbs.ollama_db import OllamaConfig, OllamaDB +from tests.conftest import * +from .utils import clear_port, nuke_dir + + +@pytest.fixture(autouse=True) +def cleanup_temp_dir(): + nuke_dir(TEMP_DIR) + TEMP_DIR.mkdir(parents=True, exist_ok=True) + yield + nuke_dir(TEMP_DIR) + TEMP_DIR.mkdir(parents=True, exist_ok=True) + + +@pytest.fixture(scope="module", autouse=True) +def cleanup_test_containers(): + client = docker.from_env() + + def _cleanup(): + for container in client.containers.list(all=True): + if container.name.startswith("test-ollama"): + try: + container.stop(timeout=5) + except docker.errors.APIError: + pass + try: + container.remove(force=True) + except docker.errors.APIError: + pass + + _cleanup() + yield + _cleanup() + + +@pytest.fixture +def clear_port_11434(): + clear_port(11434, "test-ollama") + + +@pytest.fixture +def ollama_config() -> OllamaConfig: + name = f"test-ollama-{uuid.uuid4().hex[:8]}" + ollama_data = Path(TEST_DIR, "data", "ollamadata", name) + ollama_data.mkdir(parents=True, exist_ok=True) + + return OllamaConfig( + project_name="itest", + volume_path=ollama_data, + container_name=name, + retries=30, + delay=2, + ) + + +@pytest.fixture +def ollama_manager(ollama_config: OllamaConfig): + manager = OllamaDB(ollama_config) + yield manager + + +@pytest.mark.usefixtures("clear_port_11434") +def test_create_db_and_connection(ollama_manager: OllamaDB): + container = ollama_manager.create_db() + assert isinstance(container, Container) + + response = requests.get(f"{ollama_manager.base_url}/api/tags", timeout=10) + assert response.status_code == 200 + assert "models" in response.json() + + +@pytest.mark.usefixtures("clear_port_11434") +def test_stop_db(ollama_config: OllamaConfig, ollama_manager: OllamaDB): + ollama_manager.create_db() + time.sleep(1) + ollama_manager.stop_db() + + client = docker.from_env() + containers = client.containers.list(all=True, filters={"name": ollama_config.container_name}) + assert len(containers) == 1 + assert containers[0].status in ("exited", "created") + + +@pytest.mark.usefixtures("clear_port_11434") +def test_delete_db(ollama_config: OllamaConfig, ollama_manager: OllamaDB): + ollama_manager.create_db() + with pytest.raises(RuntimeError, match="still running"): + ollama_manager.delete_db() + + ollama_manager.delete_db(running_ok=True) + client = docker.from_env() + containers = client.containers.list(all=True, filters={"name": ollama_config.container_name}) + assert len(containers) == 0 diff --git a/usage/ollama_example.ipynb b/usage/ollama_example.ipynb new file mode 100644 index 0000000..ac9ef73 --- /dev/null +++ b/usage/ollama_example.ipynb @@ -0,0 +1,171 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Ollama Manager Usage Examples\n", + "\n", + "This notebook demonstrates how to use the `OllamaDB` manager class to create, manage, and interact with an Ollama runtime in a Docker container." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "### Import Dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import uuid\n", + "from pathlib import Path\n", + "import requests\n", + "\n", + "sys.path.insert(0, str(Path().cwd().parent))\n", + "\n", + "from docker_db.dbs.ollama_db import OllamaConfig, OllamaDB" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an Ollama Instance\n", + "\n", + "Let's create a temporary directory for Ollama data and define a container configuration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "temp_dir = Path(\"tmp\")\n", + "temp_dir.mkdir(exist_ok=True)\n", + "\n", + "container_name = f\"demo-ollama-{uuid.uuid4().hex[:8]}\"\n", + "\n", + "config = OllamaConfig(\n", + " project_name=\"demo\",\n", + " container_name=container_name,\n", + " workdir=temp_dir.absolute(),\n", + " retries=30,\n", + " delay=2,\n", + ")\n", + "\n", + "db_manager = OllamaDB(config)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Start the Runtime\n", + "\n", + "We'll now create and start the Ollama runtime." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "db_manager.create_db()\n", + "print(f\"Ollama started successfully in container '{container_name}'\")\n", + "print(\"Connection details:\")\n", + "print(f\" Host: {config.host}\")\n", + "print(f\" Port: {config.port}\")\n", + "print(f\" Base URL: {db_manager.connection_string()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Query the Ollama API\n", + "\n", + "Now let's call the API and inspect the available local models." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = requests.get(f\"{db_manager.base_url}/api/tags\", timeout=10)\n", + "response.raise_for_status()\n", + "payload = response.json()\n", + "\n", + "models = payload.get(\"models\", [])\n", + "print(f\"Available local models: {len(models)}\")\n", + "for model in models:\n", + " print(\" -\", model.get(\"name\", \"unknown\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clean Up\n", + "\n", + "When you're done with the runtime, remove the container." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "db_manager.delete_db(running_ok=True)\n", + "print(f\"Ollama container '{container_name}' deleted\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "This notebook demonstrated how to:\n", + "\n", + "1. Configure and create an Ollama runtime with `OllamaDB`\n", + "2. Verify readiness via the Ollama HTTP API\n", + "3. Inspect available local models\n", + "4. Clean up the container when finished" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/usage/tmp/oldata/id_ed25519 b/usage/tmp/oldata/id_ed25519 new file mode 100644 index 0000000..c511d86 --- /dev/null +++ b/usage/tmp/oldata/id_ed25519 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz +c2gtZWQyNTUxOQAAACCiieUSI9C0jNq+JZ66LVqb5/6c55ReUfkLY3rvk97yAgAA +AIjCLMZ7wizGewAAAAtzc2gtZWQyNTUxOQAAACCiieUSI9C0jNq+JZ66LVqb5/6c +55ReUfkLY3rvk97yAgAAAEDxv3oUS3gwauGBZ5eze0qLf/U592RgN4KP4aKjSihK +raKJ5RIj0LSM2r4lnrotWpvn/pznlF5R+Qtjeu+T3vICAAAAAAECAwQF +-----END OPENSSH PRIVATE KEY----- diff --git a/usage/tmp/oldata/id_ed25519.pub b/usage/tmp/oldata/id_ed25519.pub new file mode 100644 index 0000000..6eee742 --- /dev/null +++ b/usage/tmp/oldata/id_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKKJ5RIj0LSM2r4lnrotWpvn/pznlF5R+Qtjeu+T3vIC From 6e504184eb9a6be416afb2d3f3c4e83a8b0738f1 Mon Sep 17 00:00:00 2001 From: Amadou Wolfgang Cisse Date: Mon, 2 Mar 2026 22:12:07 +0100 Subject: [PATCH 2/5] Adding ollama skill --- .github/workflows/cicd.yml | 3 +- .skill-planning/ai-narrative-plan.md | 197 ----------------- .skills/generate-documentation-intro.md | 196 ----------------- .skills/generate-readme.md | 215 ------------------- .skills/targeted-marketing.md | 270 ------------------------ README.md | 57 +++-- docker_db/docker/model.py | 5 +- docs/introduction.rst | 10 +- docs/quick_start.rst | 4 + pyproject.toml | 5 + 10 files changed, 62 insertions(+), 900 deletions(-) delete mode 100644 .skill-planning/ai-narrative-plan.md delete mode 100644 .skills/generate-documentation-intro.md delete mode 100644 .skills/generate-readme.md delete mode 100644 .skills/targeted-marketing.md diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index dee5ded..ec757c4 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -27,6 +27,7 @@ jobs: - tests/test_ollama.py - tests/test_redis.py - tests/test_opensearch.py + - tests/test_neo4j.py steps: - name: Checkout @@ -54,7 +55,7 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - python -m pip install -e ".[test,rag]" + python -m pip install -e ".[test,rag,graph]" - name: Register Jupyter kernel run: | diff --git a/.skill-planning/ai-narrative-plan.md b/.skill-planning/ai-narrative-plan.md deleted file mode 100644 index 8729b4d..0000000 --- a/.skill-planning/ai-narrative-plan.md +++ /dev/null @@ -1,197 +0,0 @@ -# py-dockerdb — AI Narrative Arc Plan - -> **Core thesis**: Every serious AI application needs infrastructure. That infrastructure is painful to set up. py-dockerdb makes it a one-liner — locally, reproducibly, in Python. - ---- - -## The Narrative Arc - -``` -[Foundation] [Vector Layer] [Knowledge Layer] [Inference Layer] -Postgres/MySQL →→ pgvector / Redis →→ Neo4j (GraphRAG) →→ Ollama / LocalAI -MongoDB/MSSQL Redis Stack Elasticsearch ↑ - ↓ [Streaming Layer] - [Observability] Kafka / Redis Streams - TimescaleDB / InfluxDB -``` - -The library becomes the answer to: *"I just want to experiment with [hot AI pattern] without fighting Docker"* - ---- - -## Phase 0 — Ship What's In-Flight (feat_redis) - -**Status**: Branch exists, most code written. Needs polish + story. - -| Task | Output | -|---|---| -| Finalize `RedisDB` / `RedisConfig` | Merged, tested | -| Complete `001_pgvector_rag_example.ipynb` | Runnable end-to-end | -| Write `redis_example.ipynb` | Cache, pub/sub, streams basics | -| Update README with Redis + pgvector sections | Discoverability | -| PR → main | Release 0.8.0 | - ---- - -## Phase 1 — Complete the Vector Story - -### 1A. Redis Stack (Vector Search + Semantic Cache) - -**Why**: The plain `redis:7` image doesn't ship with RedisSearch or RedisVL. The Stack image does, and it's what LangChain's `RedisSemanticCache` and `redisvl` actually need. - -**What to build**: -- `RedisStackDB` + `RedisStackConfig` (image: `redis/redis-stack:latest`) -- Expose `RedisVL` client via `.connection` -- Notebook: **Semantic LLM response cache** — embed query → check Redis → skip OpenAI call on hit → show cost savings - -**Why it's hot**: LLM API cost reduction is a top enterprise pain point right now. - -### 1B. Elasticsearch / OpenSearch - -**Why**: Hybrid search (BM25 keyword + kNN vector) consistently beats pure vector search in production RAG benchmarks. ES is the most deployed search backend. - -**What to build**: -- `ElasticsearchDB` + `ElasticsearchConfig` (image: `elasticsearch:8`) -- `OpenSearchDB` variant (image: `opensearchproject/opensearch:2`) -- Notebook: **Hybrid RAG** — index docs with both dense vectors and BM25, use Reciprocal Rank Fusion, compare vs pgvector-only - -**Why it's hot**: "Hybrid search" is one of the top RAG improvement techniques of 2024–2025. - ---- - -## Phase 2 — Knowledge Graph Layer (GraphRAG) - -### Neo4j - -**Why**: Microsoft Research's GraphRAG paper (2024) triggered massive interest in graph-based retrieval. Neo4j is the dominant graph database with a production Docker image. LlamaIndex and LangChain both have Neo4j integrations. - -**What to build**: -- `Neo4jDB` + `Neo4jConfig` (image: `neo4j:5`, default bolt port 7687 + HTTP 7474) -- Expose `neo4j.GraphDatabase.driver()` via `.connection` -- Notebook: **GraphRAG pipeline** — extract entity relationships from documents → store as graph → traverse graph for context-enriched retrieval → compare answer quality vs naive RAG - -**Why it's hot**: GraphRAG is the current frontier of RAG research. Having a local Neo4j in one call is the exact pain point developers complain about. - -### Apache AGE (Bonus — Postgres Graph Extension) - -- Similar Dockerfile pattern to pgvector -- Enables graph queries inside Postgres — reduces infra footprint for smaller projects -- Lower priority than Neo4j but tells a good "Postgres does everything" story - ---- - -## Phase 3 — LLM Inference Layer - -### Ollama - -**Why**: This is the wildcard that could 10x the library's audience. Ollama is the de facto standard for running local LLMs (Llama 3, Mistral, Phi, Gemma). It ships as a Docker image. Managing it the same way you manage a database — `OllamaManager.create_db()`, `manager.connection` returns an OpenAI-compatible client — is a genuinely novel idea. - -**What to build**: -- `OllamaManager` + `OllamaConfig` (image: `ollama/ollama:latest`) -- `.pull_model(model_name)` method (calls Ollama pull API) -- `.connection` returns an `openai.OpenAI(base_url="http://localhost:11434/v1")` client -- Notebook: **Full local AI stack** — `OllamaManager` + `pgvector PostgresDB` + `Neo4jDB` = complete RAG system, zero cloud, zero cost - -**Why it's hot**: "Run AI locally" is the top developer interest category right now. This would make the library's story: *"The entire AI infrastructure stack, locally, in Python."* - -### LocalAI (Alternative/Complement) - -- OpenAI-compatible API, broader model format support (GGUF, GPTQ) -- Docker image: `localai/localai:latest` -- Lower priority than Ollama but worth noting as a variant - ---- - -## Phase 4 — Streaming & Async Pipelines - -### Apache Kafka - -**Why**: AI agents that call tools, process results, and chain actions are inherently async. Kafka is the standard message backbone for production AI pipelines. LangChain and LlamaIndex both have Kafka integrations. - -**What to build**: -- `KafkaManager` + `KafkaConfig` (image: `apache/kafka:3`) -- Note: Kafka needs ZooKeeper or KRaft mode — use KRaft (built-in since Kafka 3.3) to keep it single-container -- `.connection` returns `confluent_kafka.Producer` / `Consumer` -- Notebook: **Async AI agent pipeline** — tasks flow through Kafka topics → multiple agent workers consume → results written to Postgres - -**Why it's hot**: Agentic AI systems need reliable task queues. This is the infrastructure glue nobody wants to configure manually. - ---- - -## Phase 5 — Observability Layer - -### TimescaleDB - -**Why**: Monitoring LLM latency, token costs, embedding drift, and eval scores over time is a time-series problem. TimescaleDB is Postgres + time-series — same Dockerfile pattern as pgvector. - -**What to build**: -- `TimescaleDB` variant of `PostgresDB` (image: `timescale/timescaledb:latest-pg16`) -- Notebook: **LLM observability dashboard** — log every LLM call (latency, tokens, cost) to TimescaleDB → query rolling averages → detect regressions - -**Why it's hot**: LLMOps/MLOps observability is a top enterprise priority. - -### InfluxDB (Alternative) - -- Purpose-built time series, less SQL-familiar but simpler for pure metrics -- Image: `influxdb:2` -- Useful if Timescale feels too Postgres-heavy - ---- - -## Priority Ranking (Impact vs Effort) - -| Priority | Addition | Effort | AI Narrative Fit | Audience | -|---|---|---|---|---| -| 🔴 Now | pgvector RAG notebook polish | Low | Vector RAG | All AI devs | -| 🔴 Now | Redis Stack semantic cache | Medium | Cost reduction | Enterprise | -| 🟠 Next | Neo4j + GraphRAG notebook | Medium-High | GraphRAG frontier | Research-aware devs | -| 🟠 Next | Elasticsearch hybrid search | Medium | Production RAG | ML engineers | -| 🟡 Soon | Ollama manager | High | Local AI stack | Indie/hobbyist devs | -| 🟡 Soon | TimescaleDB observability | Medium | LLMOps | MLOps engineers | -| 🟢 Later | Kafka async pipelines | High | Agentic AI | Platform engineers | -| 🟢 Later | Apache AGE | Low | Postgres story | Postgres fans | - ---- - -## Content & Discoverability Plan - -The library needs a **notebook series** that tells the story end-to-end: - -| Notebook | Concept | Tags | -|---|---|---| -| `001_pgvector_rag_example.ipynb` | Basic vector RAG | rag, pgvector, llamaindex | -| `002_redis_semantic_cache.ipynb` | LLM semantic caching | redis, langchain, cost | -| `003_hybrid_search_rag.ipynb` | BM25 + vector hybrid | elasticsearch, rag, search | -| `004_graphrag_neo4j.ipynb` | GraphRAG pipeline | neo4j, graphrag, knowledge-graph | -| `005_local_ai_stack.ipynb` | Full local stack | ollama, pgvector, neo4j, local-llm | -| `006_llmops_observability.ipynb` | Monitoring LLM calls | timescaledb, mlops, metrics | -| `007_agent_pipeline_kafka.ipynb` | Async agent pipeline | kafka, agents, async | - -Each notebook = a standalone shareable artifact (HuggingFace, Medium, Reddit r/LocalLLaMA). - ---- - -## Positioning - -**Tagline candidates**: -- *"The entire local AI infrastructure stack, in one `pip install`"* -- *"Stop fighting Docker. Start building AI."* -- *"pgvector, Neo4j, Redis, Ollama — one Pythonic API"* - -**Comparison table for README**: - -| Tool | What you'd normally do | With py-dockerdb | -|---|---|---| -| pgvector | Write Dockerfile, init SQL, manage container | `PostgresDB(PostgresConfig(dockerfile_path="Dockerfile.pgvector")).create_db()` | -| Neo4j | Pull image, set env vars, wait for bolt port | `Neo4jDB(Neo4jConfig()).create_db()` | -| Ollama | Install Ollama, manage process, remember flags | `OllamaManager(OllamaConfig(model="llama3")).create_db()` | -| Redis Stack | Pull stack image, configure modules | `RedisStackDB(RedisStackConfig()).create_db()` | - ---- - -## Open Questions - -1. **Scope boundary**: Does Ollama fit the "database management" framing, or should it live in a separate but related library? (Suggestion: keep it in, rename the library conceptually to "AI infrastructure manager") -2. **Docker Compose support**: Some stacks (Kafka + ZooKeeper, Weaviate + its deps) are multi-container. Worth adding a `ComposeManager` base class? -3. **GPU containers**: Ollama on GPU requires `--gpus all` flag — is that in scope? -4. **Hosted vector DBs**: Should there be a `QdrantDB` (purpose-built vector DB, single Docker container, excellent Python SDK) — probably yes, lower effort than Neo4j. diff --git a/.skills/generate-documentation-intro.md b/.skills/generate-documentation-intro.md deleted file mode 100644 index b067be2..0000000 --- a/.skills/generate-documentation-intro.md +++ /dev/null @@ -1,196 +0,0 @@ ---- -name: sphinx-intro-docs -description: > - Generates the introductory Sphinx documentation section for a Python repository: - a landing page and a quickstart guide. Anchors both files to the confirmed audience - and framing from a marketing prose output, and derives quickstart examples directly - from usage notebooks rather than inventing them. - Use this skill whenever the user wants to create or improve the introductory part of - their Sphinx docs, set up a docs landing page, write a getting-started guide, or - produce the human-readable entry point to an API reference site. Trigger even if the - user phrases it as "write the intro for my docs", "make a quickstart page", or - "set up the first page of my documentation". ---- - -# Sphinx Intro Docs - -Generates two files that form the introductory layer of a Sphinx documentation site: -a landing page and a quickstart guide. Both are derived from existing project artefacts -rather than written from scratch, ensuring the docs stay consistent with the README and -marketing positioning. - ---- - -## Inputs to gather - -Read these before writing anything. They are listed in priority order. - -| Input | Where to find it | Required | -|---|---|---| -| Marketing prose output | `repo_marketing_prose.md` in project root or outputs dir | Yes | -| README.md | Project root | Yes | -| Usage notebooks | `/usage/*.ipynb` | Strongly preferred | -| Existing Sphinx conf.py | `docs/conf.py` | Preferred | -| Existing toctree structure | `docs/index.rst` or `docs/introduction.rst` | If present | - -If no marketing prose file is found, ask the user to run the `repo-marketing-prose` -skill first. The landing page without confirmed positioning will default to generic copy -that serves no audience. - -If no usage notebooks are found, fall back to the README usage section, but flag this -to the user — the quickstart will be weaker without runnable notebook examples. - ---- - -## What to extract from each input - -**From marketing prose:** -- Confirmed target audience and any secondary audience -- Keyword list -- Hero section (use verbatim on the landing page) -- Elevator pitch for each audience card — these inform the "Who is this for" section -- "Where they gather" from each card — useful context for tone but not written into docs - -**From README.md:** -- The single canonical usage example — reuse it in the quickstart rather than - rewriting it. Keep the code identical; only adjust surrounding prose if needed. -- The "When to use this" one-liners — adapt (do not copy verbatim) for the landing page -- Prerequisites and installation steps — copy these exactly into the quickstart - -**From usage notebooks:** -- Read markdown cells to understand the narrative arc of each notebook -- Extract the first complete workflow from the most beginner-friendly notebook - (prefer a notebook named `quickstart`, `intro`, or `example` over engine-specific ones) -- Note any "what next" or "see also" references at the end of notebooks — use these - to populate the "Next steps" section of the quickstart - ---- - -## Phase 1: Landing Page - -### File -Produce `docs/introduction.rst` (or `.md` if the project uses MyST — check `conf.py` -for `myst_parser` in extensions). - -### Structure - -``` -[Hero section from marketing prose — verbatim] - -Who is this for ---------------- -Short paragraph per confirmed audience (primary first, secondary second). -Draw from the audience card elevator pitches. 2-3 sentences each. -Do not list features here — this section answers "is this for me?". - -What you can do with {project name} ------------------------------------- -Adapt the "When to use this" one-liners from the README into a slightly fuller -form — one short paragraph per use case, not a bullet list. -Each paragraph should name the context, the action, and the payoff. -Use keywords from the marketing prose naturally. - -Where to go next ----------------- -A toctree or a short set of links: -- Quickstart (link to quickstart page) -- API Reference (link to modules page if it exists) -- Usage notebooks (link to /usage directory or rendered notebook pages) -Keep this section minimal — it is navigation, not content. -``` - -### Tone -Match the confirmed audience tone from the marketing prose. The landing page sits -between the README (persuasive) and the quickstart (instructional) — it should feel -welcoming and orienting, not salesy and not yet technical. - ---- - -## Phase 2: Quickstart Guide - -### File -Produce `docs/quick_start.md` (MyST) or `docs/quick_start.rst` depending on project -setup. Check `conf.py` — if `myst_parser` is in extensions, use `.md`. - -### Structure - -``` -# Quickstart - -One sentence: what the reader will have working by the end of this page. - -## Prerequisites -[Copy exactly from README prerequisites section] - -## Installation -[Copy exactly from README installation section] - -## Your first {project name} workflow -[Single canonical example from README, identical code] -Brief prose before the code block explaining what is about to happen. -Brief prose after explaining what just ran and what the output means. - -## What just happened -2-4 sentences explaining the pattern the example demonstrated — config, create, -connect, teardown in the case of py-dockerdb. This is the one place where a -core concept is introduced, but only the concept directly visible in the example. -Do not introduce abstractions that are not in the code block. - -## Next steps -- Link to each usage notebook by name with a one-line description of what it covers -- Link to the API reference if it exists -- Link to any configuration or debug guides if they exist -``` - -### On the code example -Use the identical code from the README canonical example. Do not simplify, shorten, -or abstract it — a quickstart that runs is more valuable than one that is tidy. -If the example requires a teardown step, include it and note why (leaked containers, -cleanup, reproducibility — whatever is relevant to the confirmed audience). - -### Tone -Instructional and direct. The reader has committed to trying the tool. Minimize -explanation before the first code block — they want to run something, not read. -Save explanation for after the code has run. - ---- - -## Output files - -Save both files to the `docs/` directory (or wherever the existing Sphinx source -files live — check `conf.py` for `source_dir` if present). - -After generating both files, update the toctree immediately so the pages appear in -sidebar navigation on the next build. Do not leave this to the user. - -Find the relevant toctree file — typically `docs/index.rst` or `docs/introduction.rst`. -If an `introduction.rst` toctree exists, add entries there. If only `index.rst` exists, -add them there. If neither exists, create a minimal `introduction.rst` that wraps both -files and add a reference to it in `index.rst`. - -Add entries in this order: landing page first, quickstart second: - -```rst -.. toctree:: - :maxdepth: 2 - - introduction - quick_start -``` - -If the toctree already has entries, insert the new ones at the top of the relevant -toctree block so the intro section leads the navigation. - -After updating, tell the user exactly which file was modified and what was added. - ---- - -## Writing style - -- No em dashes -- Active voice -- No marketing superlatives ("powerful", "robust", "seamless") -- RST: use underline-only headings (`===`, `---`, `~~~`) consistent with existing files -- MyST: use standard `#` heading hierarchy -- Code blocks must specify language (` ```python `, ` ```bash `, etc.) -- Cross-references to other doc pages use `:doc:` in RST or relative links in MyST \ No newline at end of file diff --git a/.skills/generate-readme.md b/.skills/generate-readme.md deleted file mode 100644 index b240b43..0000000 --- a/.skills/generate-readme.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -name: readme-generator -description: Generates comprehensive README.md files for software projects by analyzing codebase structure -allowed-tools: ["Read", "Glob", "Grep", "Write"] -version: 1.1.0 -author: GLINCKER Team -license: Apache-2.0 -keywords: [documentation, readme, markdown, project] ---- - -# README Generator - -Automatically generates professional, comprehensive README.md files by analyzing your -project structure, dependencies, usage examples, and marketing prose. - -## What This Skill Does - -This skill helps you create high-quality README files by: -- Anchoring the framing and tone to a confirmed marketing prose output throughout the - entire document, not just the opening -- Propagating the confirmed audience, keywords, and all audience cards into the structure - and content of every section -- Extracting real usage patterns from notebooks in the `/usage` directory -- Analyzing project structure and identifying key components -- Detecting programming languages and frameworks -- Generating appropriate sections with relevant content -- Following README best practices - ---- - -## Instructions - -### 0. Priority Inputs (read these first, before anything else) - -**Marketing prose output** — look for `repo_marketing_prose.md` in the project root or -outputs directory. If found, extract and hold all of the following in mind for the -entire README, not just the opening section: - -- **Confirmed target audience** — every section should be written for this person. - Their vocabulary, their pain points, their level of familiarity with the tooling. -- **Keyword list** — these terms must appear naturally across the README body: in - section headings where relevant, in the first sentence of the Usage section, in any - "When to use this" or workflow descriptions. Do not cluster them in one place. -- **README Hero Section** — use verbatim as the opening. Do not rewrite or paraphrase. -- **Audience Cards (all of them, including unchosen ones)** — read the elevator pitch - and pain point of each card. Use the primary audience card to drive the "When to use - this" section. Use the secondary audience card (if one was confirmed) to add a - secondary workflow. Use any remaining cards to identify use cases worth naming even - if they are not the lead framing — for example, a RAG audience card means "RAG - prototype" belongs in the usage examples and keyword appearances even if it is not - the primary audience. -- **Social share blurb** — the core problem statement here should be echoed in the - introduction paragraph that immediately follows the hero. - -If no marketing prose file is found, ask the user before proceeding. Generating a README -without confirmed positioning produces generic copy that serves no audience well. - -**Usage notebooks** — check the `/usage` directory for `.ipynb` files. For each notebook: -- Read the markdown cells for intent and narrative -- Extract code cells as usage examples, preserving the actual API calls -- Note which engines, configurations, or workflows are demonstrated -- Prefer notebook examples over invented ones in the Usage section -- Check each example for completeness: does it show setup, use, AND teardown? If - teardown (stop, delete, cleanup) is present in the notebook, include it. If it is - missing from the example but present in the codebase, add it with a note. - ---- - -### 1. Project Discovery - -After reading the priority inputs, analyze the project structure: -- Use Glob to find key files: `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod` -- Use Glob to identify main source directories -- Use Read to examine configuration files -- Use Grep to find test files and CI configuration - -### 2. Content Analysis - -Based on findings, determine: -- Project type (library, application, CLI tool, etc.) -- Primary programming language(s) -- Dependencies and frameworks -- Build and test commands -- License type (from LICENSE file) - ---- - -### 3. README Structure - -Build the README in this order. Sections marked **required** must always appear. -Sections marked **audience-driven** must appear if the marketing prose supports them. -Sections marked **optional** appear only if relevant project files exist. - -#### [required] Hero -Use the hero section from marketing prose verbatim. Do not alter it. - -#### [required] Introduction paragraph -One short paragraph (3-5 sentences) drawn from the social share blurb and the primary -audience card's pain point. This is the narrative bridge between the hero and the -technical content. It should answer "is this for me?" for the confirmed audience. - -#### [audience-driven] When to use this -A short section with 3-5 one-liners, one per audience card (primary, secondary, and -any remaining cards that represent valid use cases). Each line names the context and -the payoff. Example format: - -- **Teaching a SQL workshop** — seed classroom datasets automatically and skip the - environment setup section entirely. -- **Comparing databases for an MVP** — switch engines without changing connection code. -- **Local RAG prototype** — spin up a backing store, test it, swap it out in one - config change. - -Order by: primary audience first, secondary audience second, remaining audiences last. -Use the keywords from the marketing prose naturally in these lines. - -#### [required] Prerequisites -Software and system requirements. - -#### [required] Installation - -#### [required] Usage -Lead the section with one sentence that names the confirmed audience and the primary -use case, using keywords from the marketing prose. - -Then present a single canonical example drawn from the primary audience's most likely -entry point (for an instructor audience, use PostgreSQL; for an MVP audience, use -whichever engine is most prominent in the notebooks). The example must show setup, -core usage, AND teardown as a complete lifecycle. - -After the single example, add a "More examples" subsection that links directly to each -notebook in the `/usage` directory by filename. Do not reproduce the other engine -examples inline — the notebooks are the right place for that detail. - -Surface any notably useful capabilities (init scripts, connection string helpers, -engine switching) as named subsections with a brief description and a short snippet -only if the snippet is 5 lines or fewer. Order subsections by relevance to the -confirmed audience, not by implementation order. - -#### [optional] Development -How to set up for development. Include only if a dev setup process exists. - -#### [optional] Testing -How to run tests. Include only if a test suite exists. - -#### [optional] Contributing - -#### [required] License - ---- - -### 4. Writing Style - -- Clear, concise language -- Active voice -- No em dashes -- Code blocks with proper syntax highlighting -- Badge shields for status indicators (if CI/CD detected) -- No emoji unless user requests -- Tone must match the confirmed audience from marketing prose: - -| Audience type | Tone | -|---|---| -| Instructor / educator | Practical, time-saving framing, classroom-aware | -| Enterprise / Azure | Professional, precise, productivity-focused | -| ML / RAG builder | Technical, experiment-friendly, iteration-aware | -| MVP / early-stage builder | Direct, low-overhead, decision-reducing | - ---- - -### 5. Output - -Present the generated README to the user and offer to: -- Write it to README.md -- Make adjustments based on feedback -- Add additional sections - ---- - -## Configuration - -This skill adapts to project type: - -| Project Type | Key Files | Focus Areas | -|---|---|---| -| Python | `pyproject.toml`, `setup.py` | pip install, virtual env | -| Node.js | `package.json` | npm install, scripts | -| Rust | `Cargo.toml` | cargo build, features | -| Go | `go.mod` | go get, modules | -| Generic | None | Basic structure | - -## Tool Requirements - -- **Read**: Examine configuration, source files, marketing prose, and notebooks -- **Glob**: Find relevant files across project, including `/usage/*.ipynb` -- **Grep**: Search for patterns (tests, CI, etc.) -- **Write**: Create the README.md file - -## Limitations - -- Cannot include screenshots (user must add manually) -- May miss custom build processes not in standard files -- Generates a starting point - user should review and customize -- Works best with standard project structures -- Does not analyze actual code logic for features - -## Error Handling - -- **No marketing prose found**: Ask user to run the `repo-marketing-prose` skill first, - or confirm they want a generic README with no audience targeting -- **No usage notebooks found**: Fall back to docstring examples and quickstart docs, - note to user that `/usage` notebooks would improve the examples section -- **No project files found**: Ask user to confirm working directory -- **Multiple languages detected**: Generate sections for each, note polyglot nature -- **Existing README**: Prompt user before overwriting, offer to merge -- **Missing key info**: Generate placeholder sections with TODO markers \ No newline at end of file diff --git a/.skills/targeted-marketing.md b/.skills/targeted-marketing.md deleted file mode 100644 index 3bc86bc..0000000 --- a/.skills/targeted-marketing.md +++ /dev/null @@ -1,270 +0,0 @@ ---- -name: repo-marketing-prose -description: > - Generates SEO-optimised marketing copy for open-source or internal Python repositories. - Produces a full suite of prose assets: a README hero section, a PyPI / package-index - long description, GitHub repository "About" one-liner, platform-specific social-share - blurbs, a keyword-rich meta description for documentation sites, GitHub topic tags, - and PyPI classifier blocks. - Use this skill whenever the user wants to make a repository more discoverable, attractive, - or professional-looking — even if they phrase it as "write a README", "make my repo look - good", "help me get more stars", "write a description for PyPI", or "how do I market my - open-source project". Trigger on requests that explicitly touch repo presentation, - discoverability, or developer audience growth. ---- - -# Repo Marketing Prose - -Generates a cohesive set of marketing and SEO copy for a software repository by reading -the existing project artefacts and synthesising them into prose that is accurate, -developer-friendly, and optimised for search. - ---- - -## Inputs to gather - -Before writing, collect as much of the following as is available. Read files directly -from the repository or ask the user to paste them. - -| Input | Where to find it | Required | -|---|---|---| -| Project name and tagline | `pyproject.toml`, `setup.py`, existing README | Yes | -| What the project does (one sentence) | README, docstrings, `__init__.py` | Yes | -| Primary user persona | README, docs motivation section | Strongly preferred | -| Key features (3-5 bullet points worth) | README, docs, changelog | Strongly preferred | -| Tech stack / dependencies | `pyproject.toml`, `requirements.txt` | Strongly preferred | -| Existing README | Filesystem | If present | -| Existing docs (motivation, quickstart) | Filesystem or uploaded files | If present | -| Example usage notebooks | `/usage` directory (`.ipynb` files) | Optional | -| Competitor or comparable tools | User input | Optional | -| Target keywords | User input | Optional | - -If the user has already shared files in the conversation (e.g. a motivation doc, quickstart, -conf.py), extract what you need from those first before asking. - ---- - -## Outputs to produce - -Never add any of these: — - -Always produce all seven assets in a single response, clearly separated by headings. -Save them to a single Markdown file so the user can copy-paste from one place. - -### 1. README Hero Section -The first ~20 lines of a README. Includes: -- H1 project name with a one-line tagline -- A `pip install {package-name}` code block immediately after the tagline -- A 2-3 sentence elevator pitch (what it is, who it is for, what makes it different) -- A shield/badge line (build, PyPI version, licence) — use placeholder URLs the user - can fill in -- A short "Why this project?" paragraph (3-5 sentences) that frames the problem, - positions the tool relative to alternatives, and names at least one comparable tool - by name if competitors were provided - -### 2. PyPI Long Description Intro -A 100-150 word prose block suitable for the top of a `pyproject.toml` description -or a PyPI project page. Must be plain prose (no headers, no bullet lists) because -PyPI renders the full README but search engines index only the first ~150 words. -Front-load the most important keywords naturally. - -### 3. GitHub "About" One-Liner -A single sentence of 120-160 characters for the repository's About field. -Must include: what the tool does, primary tech context (e.g. "for Azure Data Factory"), -and one differentiating adjective. No marketing superlatives ("best", "ultimate"). - -### 4. Social Share Blurbs -Three platform-specific variants. Each mentions the problem it solves and ends with -a call to action. - -- **Twitter/X**: Max 280 characters. Punchy, no hashtags unless they are the canonical - community tag (e.g. `#rstats`). Link at the end. -- **LinkedIn**: 3-4 sentences. Professional tone, can name-drop the tech stack. Ends - with a question or invitation to comment. -- **Hacker News "Show HN"**: One sentence title in `Show HN: {title}` format, followed - by 2-3 sentences of plain prose context. No hype. What it does, why you built it, - where to find it. - -### 5. Documentation Meta Description -A 150-160 character string for the `html_meta` or `description` field in Sphinx `conf.py` -or a `` tag. Must naturally include the project name and primary -use-case keyword. - -### 6. GitHub Topics -8-12 lowercase hyphenated tags for the repository's Topics field. Topics are the primary -GitHub browse/discovery mechanism. Include: language (`python`), infrastructure type, -primary use-case, and any framework integrations present in the codebase. -Format as a comma-separated list the user can paste directly into GitHub's Topics field. - -### 7. PyPI Classifiers -A ready-to-paste `classifiers = [...]` block for `pyproject.toml`. Include at minimum: -development status, intended audience, licence, programming language versions, and -topic classifiers that match the project's primary use-case. Use only valid -[PyPI trove classifiers](https://pypi.org/classifiers/). - ---- - -## SEO principles to apply - -- **Front-load keywords**: The most important search terms (project name, primary verb, - primary noun) belong in the first sentence of every asset, not buried mid-paragraph. -- **Use the words developers search for**: Prefer "deploy SQL pipelines to Azure Data Factory" - over "manage your data transformation workflows in the cloud." -- **Be specific over superlative**: "Generates one ADF pipeline per dbt model" ranks and - converts better than "powerful, flexible solution." -- **Mirror competitor vocabulary**: If users search for "dbt alternative" or "ADF pipeline - builder", those phrases should appear naturally in the copy. If competitors were provided, - name at least one in the README hero's "Why this project?" paragraph — developers - searching for alternatives will find the repo via those names. -- **Avoid keyword stuffing**: Each keyword cluster should appear 2-3 times across the full - asset set, not repeatedly in a single paragraph. -- **Active voice throughout**: "Deploy your models with one command" not "Models can be - deployed using a single command." - ---- - -## Tone guidelines - -The tone should match the project's apparent audience. Infer from the tech stack: - -| Stack signals | Tone | Emoji | -|---|---|---| -| Azure / enterprise tooling | Professional, precise, productivity-focused | Never | -| ML / research libraries | Technical, peer-to-peer, understated | Rarely, only in social blurbs | -| CLI / developer tools | Direct, terse, shows-don't-tells | Never | -| Data engineering | Pragmatic, problem-first, ops-aware | Never | -| AI / LLM developer tooling | Practical, local-first workflow, notebook-centric examples | Occasionally in social blurbs only | - -If an existing README is present, match its emoji style exactly — do not introduce or -remove emoji relative to what the author already uses. - -Never use: "cutting-edge", "revolutionary", "game-changing", "seamlessly", "leverage" -(as a verb), "robust solution", "best-in-class". - ---- - -## Workflow - -The workflow has two mandatory phases. **Do not generate artifacts until the user has -confirmed an audience at the end of Phase 1.** - -### Phase 1: Audience Discovery - -1. Read all available project files (README, motivation docs, quickstart, pyproject.toml, - conf.py, __init__.py). Note: the user may have already shared these in the conversation. -2. Check the `/usage` directory for example notebooks (`.ipynb` files). Scan their cell - sources for concrete usage patterns, API entry points, and real-world use cases — - these reveal what users actually do with the tool, not just what the author intended. -3. Identify 2-4 plausible audience segments. Think beyond the obvious intended user — - look for adjacent communities who share the same pain point but arrive from a different - background or toolchain. For example, a tool built for data engineers might be equally - compelling to analytics engineers, DevOps teams managing data infrastructure, or - backend developers new to the data space. -4. For each candidate audience, produce an **Audience Card**: - - ``` - ### Audience: {name} - Who they are: 1-2 sentences on their role, background, and daily context. - Their pain point: The specific problem this tool solves for them. - Why this framing works: What makes the tool compelling to this group specifically. - Where they gather: 2-4 concrete channels — subreddits, newsletters, Slack/Discord - communities, conferences, YouTube channels, LinkedIn groups, Hacker News tags, etc. - Elevator pitch: 2-3 sentences written in the voice and vocabulary of this audience. - Keywords they search for: 4-6 terms this group would actually type into Google or GitHub. - ``` - -5. Present all Audience Cards and ask the user to either: - - Pick one audience to target exclusively, and confirm or adjust its keyword list - - Pick a primary and secondary audience (copy will lead with one, not alienate the other), - and confirm or adjust the combined keyword list - - Propose a different audience not on the list - - Include the keyword list for each candidate audience in this message so the user can - adjust them in a single reply. Do not ask for keyword confirmation in a separate step. - -### Phase 2: Full Artifact Generation - -Only begin after the user has confirmed an audience and keywords in Phase 1. - -6. Write all seven assets anchored to the confirmed audience framing and keyword list. -7. Append all Phase 1 Audience Cards (including the ones not chosen) under an - "Audience Cards Considered" section at the bottom of the output file. -8. Save to `.skill-planning/marketing_prose.md` relative to the project root, and - present the file path to the user. - ---- - -## Output file format - -```markdown -# Marketing Prose: {Project Name} - -**Target audience:** {confirmed audience name} -**Keyword targets:** {comma-separated list} - ---- - -## 1. README Hero Section - -{content} - ---- - -## 2. PyPI Long Description Intro - -{content} - ---- - -## 3. GitHub "About" One-Liner - -{content} - ---- - -## 4. Social Share Blurbs - -### Twitter/X -{content} - -### LinkedIn -{content} - -### Hacker News "Show HN" -{content} - ---- - -## 5. Documentation Meta Description - -{content} - ---- - -## 6. GitHub Topics - -{comma-separated list} - ---- - -## 7. PyPI Classifiers - -```toml -classifiers = [ - {entries} -] -``` - ---- - -## Audience Cards Considered - -> These were the candidate audiences evaluated in Phase 1. Preserved here so you can -> revisit targeting without starting from scratch. - -### Audience: {name} -... - -### Audience: {name} -... -``` diff --git a/README.md b/README.md index 9c50ac4..ee76a34 100644 --- a/README.md +++ b/README.md @@ -6,26 +6,18 @@ [![PyPI](https://img.shields.io/pypi/v/py-dockerdb)](https://pypi.org/project/py-dockerdb/) [![License](https://img.shields.io/badge/license-MIT-lightgrey)](./LICENSE) -`py-dockerdb` gives you easy Docker database setup in Python for PostgreSQL, MySQL, MongoDB, Microsoft SQL Server, and Redis. It is built for people who teach, demo, and prototype with notebooks or scripts and need repeatable local databases in minutes. Instead of writing Docker commands and per-engine setup code, you use one API to create, start, connect, and clean up containers. +`py-dockerdb` gives you easy Docker database setup in Python for PostgreSQL, MySQL, MongoDB, Microsoft SQL Server, Redis, and Neo4j. It is built for people who teach, demo, and prototype with notebooks or scripts and need repeatable local databases in minutes. Instead of writing Docker commands and per-engine setup code, you use one API to create, start, connect, and clean up containers. -Switch from PostgreSQL to MongoDB and back without changing a line of connection code. This makes side-by-side database comparison a first-class workflow - useful for MVPs where the right engine isn't decided yet, and for RAG experiments where you want to test one storage backend, then swap it out without rewriting environment glue. +Switch from PostgreSQL to MongoDB and back without changing a line of connection code. This makes side-by-side database comparison a first-class workflow - useful for MVPs where the right engine isn't decided yet, and for RAG and GraphRAG experiments where you want to test one storage backend, then swap it out without rewriting environment glue. If you teach SQL or data workflows, it removes the environment-setup section from your slides entirely: every student runs the same two lines and gets a working database. -<<<<<<< HEAD -The following databases are supported: - - MongoDB - - Microsoft SQL Server - - PostgreSQL - - MySQL - - Ollama (local LLM runtime) -======= ## When to use this ->>>>>>> main - **Teaching a SQL workshop or notebook tutorial** - every learner starts from the same environment with no per-machine Docker setup required. -- **Comparing databases for an MVP** - run PostgreSQL, MySQL, MongoDB, MSSQL, and Redis under the same Python interface and switch engines without rewriting connection code. +- **Comparing databases for an MVP** - run PostgreSQL, MySQL, MongoDB, MSSQL, Redis, and Neo4j under the same Python interface and switch engines without rewriting connection code. - **Building a local RAG prototype** - spin up a backing store, test your retrieval pipeline, then swap from PostgreSQL to MongoDB in one config change without touching orchestration code. +- **GraphRAG experiments** - provision a Neo4j container, build knowledge graphs from your documents, and run multi-hop traversal queries with LlamaIndex or LangChain. ## Supported Databases @@ -34,6 +26,7 @@ The following databases are supported: [![MongoDB](https://img.shields.io/badge/MongoDB-47A248?logo=mongodb&logoColor=white)](https://www.mongodb.com/docs/) [![SQL Server](https://img.shields.io/badge/SQL_Server-CC2927?logo=microsoftsqlserver&logoColor=white)](https://learn.microsoft.com/en-us/sql/sql-server/) [![Redis](https://img.shields.io/badge/Redis-DC382D?logo=redis&logoColor=white)](https://redis.io/docs/) +[![Neo4j](https://img.shields.io/badge/Neo4j-008CC1?logo=neo4j&logoColor=white)](https://neo4j.com/docs/) ## Prerequisites @@ -45,6 +38,7 @@ The following databases are supported: - `pymongo` for MongoDB - `pyodbc` for MSSQL - `redis` for Redis +- `neo4j` for Neo4j ## Installation @@ -54,7 +48,7 @@ pip install py-dockerdb ## Usage -The API is consistent across all five engines: define a config, call `create_db()`, run your workload, then tear down with `delete_db()`. +The API is consistent across all engines: define a config, call `create_db()`, run your workload, then tear down with `delete_db()`. ### PostgreSQL example @@ -91,6 +85,37 @@ conn.close() db_manager.delete_db(running_ok=True) ``` +### Neo4j / GraphRAG example + +```python +import uuid +from pathlib import Path +from docker_db.dbs.neo4j_db import Neo4jConfig, Neo4jDB + +container_name = f"demo-neo4j-{uuid.uuid4().hex[:8]}" +temp_dir = Path("tmp") +temp_dir.mkdir(exist_ok=True) + +config = Neo4jConfig( + password="demopass", + project_name="demo", + container_name=container_name, + workdir=temp_dir.absolute(), +) + +db_manager = Neo4jDB(config) +db_manager.create_db() + +driver = db_manager.connection +with driver.session() as session: + session.run("CREATE (n:Person {name: 'Alice'})") + result = session.run("MATCH (n:Person) RETURN n.name AS name") + print(result.single()["name"]) # Alice + +driver.close() +db_manager.delete_db(running_ok=True) +``` + ### More examples Full runnable notebooks for each engine are in the [`usage/`](./usage/) directory: @@ -100,6 +125,7 @@ Full runnable notebooks for each engine are in the [`usage/`](./usage/) director - [MongoDB](./usage/mongo_example.ipynb) - [MSSQL](./usage/mssql_example.ipynb) - [Redis](./usage/redis_example.ipynb) +- [Neo4j / GraphRAG](./usage/neo4j_example.ipynb) - [Container lifecycle and management](./usage/db_management_example.ipynb) ### Seeding data for demos and workshops @@ -121,9 +147,10 @@ conn_string = db_manager.connection_string(sql_magic=True) ## Roadmap -Docker-friendly vector backends that can be run locally without paid licenses: +Docker-friendly vector and graph backends that can be run locally without paid licenses: - [x] PostgreSQL + `pgvector` +- [x] Neo4j (knowledge graphs, GraphRAG) - [ ] Qdrant - [ ] Chroma - [ ] Weaviate @@ -148,6 +175,8 @@ python -m pytest -vv -s tests/test_postgres.py python -m pytest -vv -s tests/test_mysql.py python -m pytest -vv -s tests/test_mongodb.py python -m pytest -vv -s tests/test_mssql.py +python -m pytest -vv -s tests/test_redis.py +python -m pytest -vv -s tests/test_neo4j.py python -m pytest -vv -s tests/test_notebooks.py ``` diff --git a/docker_db/docker/model.py b/docker_db/docker/model.py index 527cc5d..5e8f02e 100644 --- a/docker_db/docker/model.py +++ b/docker_db/docker/model.py @@ -15,6 +15,7 @@ "ollama": "ol", "redis": "rd", "opensearch": "os", + "neo4j": "n4j", } DEFAULT_IMAGE_MAP = { @@ -24,12 +25,10 @@ "mssql": "mcr.microsoft.com/mssql/server:2022-latest", "mongodb": "mongo:6", "cassandra": "cassandra:4", -<<<<<<< HEAD "ollama": "ollama/ollama:latest", -======= "redis": "redis:7", "opensearch": "opensearchproject/opensearch:2.13.0", ->>>>>>> main + "neo4j": "neo4j:5", } class ContainerConfig(BaseModel): diff --git a/docs/introduction.rst b/docs/introduction.rst index 898ecdd..e0c96e3 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -12,9 +12,9 @@ py-dockerdb .. |License| image:: https://img.shields.io/badge/license-MIT-lightgrey :target: ../LICENSE -``py-dockerdb`` gives you easy Docker database setup in Python for PostgreSQL, MySQL, MongoDB, and Microsoft SQL Server. It is built for people who teach, demo, and prototype with notebooks or scripts and need repeatable local databases in minutes. Instead of writing Docker commands and per-engine setup code, you use one API to create, start, connect, and clean up containers. +``py-dockerdb`` gives you easy Docker database setup in Python for PostgreSQL, MySQL, MongoDB, Microsoft SQL Server, Redis, and Neo4j. It is built for people who teach, demo, and prototype with notebooks or scripts and need repeatable local databases in minutes. Instead of writing Docker commands and per-engine setup code, you use one API to create, start, connect, and clean up containers. -Switch from PostgreSQL to MongoDB and back without changing a line of connection code. This makes side-by-side database comparison a first-class workflow, useful for MVPs where the right engine is not decided yet, and for RAG experiments where you want to test one storage backend, then swap it out without rewriting environment glue. +Switch from PostgreSQL to MongoDB and back without changing a line of connection code. This makes side-by-side database comparison a first-class workflow, useful for MVPs where the right engine is not decided yet, and for RAG and GraphRAG experiments where you want to test one storage backend, then swap it out without rewriting environment glue. If you teach SQL or data workflows, it removes the environment setup section from your slides entirely: every student runs the same two lines and gets a working database. @@ -23,9 +23,9 @@ Who Is This For Python instructors and demo authors who need a classroom-ready, reproducible setup across machines and operating systems. The goal is to spend time teaching SQL, workflow design, and experimentation instead of debugging Docker setup in front of learners. -Learners and MVP builders who want to compare databases quickly with the same Pythonic workflow. You can test PostgreSQL, MySQL, MongoDB, and MSSQL under one lifecycle pattern and decide based on behavior instead of setup overhead. +Learners and MVP builders who want to compare databases quickly with the same Pythonic workflow. You can test PostgreSQL, MySQL, MongoDB, MSSQL, Redis, and Neo4j under one lifecycle pattern and decide based on behavior instead of setup overhead. -RAG and AI prototype builders who need fast local backend swaps during iteration. This workflow supports testing one database option, then changing engines without rebuilding your orchestration from scratch. +RAG and AI prototype builders who need fast local backend swaps during iteration. This workflow supports testing one database option, then changing engines without rebuilding your orchestration from scratch. Neo4j support enables GraphRAG workflows — provision a knowledge graph container and run multi-hop queries with LlamaIndex or LangChain in the same lifecycle pattern. What You Can Do With py-dockerdb -------------------------------- @@ -51,3 +51,5 @@ Where To Go Next `mysql_example.ipynb `_ `mongo_example.ipynb `_ `mssql_example.ipynb `_ + `redis_example.ipynb `_ + `neo4j_example.ipynb `_ diff --git a/docs/quick_start.rst b/docs/quick_start.rst index a4866ef..bd2a167 100644 --- a/docs/quick_start.rst +++ b/docs/quick_start.rst @@ -79,4 +79,8 @@ Next Steps MongoDB CRUD and collection examples. - `mssql_example.ipynb `_: MSSQL setup and query workflow. +- `redis_example.ipynb `_: + Redis key-value store and caching examples. +- `neo4j_example.ipynb `_: + Neo4j knowledge graph setup, node/relationship queries, and GraphRAG with LlamaIndex. - API reference: :doc:`source/modules` diff --git a/pyproject.toml b/pyproject.toml index bbceb7c..7b20e7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,11 @@ rag = [ "langchain-community", "sentence-transformers", ] +graph = [ + "neo4j", + "llama-index-graph-stores-neo4j", + "langchain-community", +] [tool.setuptools] include-package-data = true From ac470927e5ef942c972189e78e311b74fae46b29 Mon Sep 17 00:00:00 2001 From: Amadou Wolfgang Cisse Date: Mon, 2 Mar 2026 22:25:29 +0100 Subject: [PATCH 3/5] Remove neo4js test from branch --- .github/workflows/cicd.yml | 1 - docker_db/dbs/ollama_db.py | 21 ++++++--------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index ec757c4..fb49565 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -27,7 +27,6 @@ jobs: - tests/test_ollama.py - tests/test_redis.py - tests/test_opensearch.py - - tests/test_neo4j.py steps: - name: Checkout diff --git a/docker_db/dbs/ollama_db.py b/docker_db/dbs/ollama_db.py index 4cec93f..f1d9148 100644 --- a/docker_db/dbs/ollama_db.py +++ b/docker_db/dbs/ollama_db.py @@ -18,6 +18,7 @@ class OllamaConfig(ContainerConfig): """Configuration for an Ollama container.""" + database: str = Field( default="ollama", description="Compatibility field required by the base container manager API.", @@ -111,9 +112,7 @@ def _create_db( db_name: str | None = None, container: Container | None = None, ): - """ - Validate Ollama API availability and optionally pull a model. - """ + """Validate Ollama API availability and optionally pull a model.""" container = container or self.client.containers.get(self.config.container_name) container.reload() if not container.attrs.get("State", {}).get("Running", False): @@ -129,9 +128,7 @@ def _create_db( return container def _wait_for_db(self, container: Container | None = None) -> bool: - """ - Wait until Ollama API is accepting connections and ready. - """ + """Wait until Ollama API is accepting connections and ready.""" try: container = container or self.client.containers.get(self.config.container_name) for _ in range(self.config.retries): @@ -146,9 +143,7 @@ def _wait_for_db(self, container: Container | None = None) -> bool: return self._wait_for_api_ready() def pull_model(self, model: str): - """ - Pull a model via Ollama API. - """ + """Pull a model via Ollama API.""" response = requests.post( f"{self.base_url}/api/pull", json={"model": model, "stream": False}, @@ -159,9 +154,7 @@ def pull_model(self, model: str): return response.json() def list_models(self) -> list[dict]: - """ - List locally available models. - """ + """List locally available models.""" response = requests.get(f"{self.base_url}/api/tags", timeout=10) if response.status_code != 200: raise RuntimeError(f"Failed to list models: {response.text}") @@ -169,8 +162,6 @@ def list_models(self) -> list[dict]: return payload.get("models", []) def test_connection(self): - """ - Ensure Ollama API is reachable. - """ + """Ensure Ollama API is reachable.""" if not self._wait_for_api_ready(): raise ConnectionError("Ollama API is not reachable.") From 10ae890e6c105777b1d19ab0bcf37252527fe61e Mon Sep 17 00:00:00 2001 From: Amadou Wolfgang Cisse Date: Mon, 2 Mar 2026 22:53:17 +0100 Subject: [PATCH 4/5] Added docstrings --- docker_db/dbs/ollama_db.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/docker_db/dbs/ollama_db.py b/docker_db/dbs/ollama_db.py index f1d9148..e9a4d73 100644 --- a/docker_db/dbs/ollama_db.py +++ b/docker_db/dbs/ollama_db.py @@ -65,7 +65,21 @@ def base_url(self) -> str: return f"http://{self.config.host}:{self.config.port}" def connection_string(self, db_name: str = None, sql_magic: bool = False) -> str: - """Get Ollama base URL.""" + """ + Get Ollama base URL. + + Parameters + ---------- + db_name : str, optional + Unused placeholder for API compatibility with other managers. + sql_magic : bool, optional + Unused placeholder for API compatibility with SQL-oriented managers. + + Returns + ------- + str + Base URL of the Ollama API endpoint. + """ return self.base_url def _get_environment_vars(self): @@ -143,7 +157,19 @@ def _wait_for_db(self, container: Container | None = None) -> bool: return self._wait_for_api_ready() def pull_model(self, model: str): - """Pull a model via Ollama API.""" + """ + Pull a model via Ollama API. + + Parameters + ---------- + model : str + Model name to download into the local Ollama store. + + Returns + ------- + dict + JSON response returned by Ollama pull endpoint. + """ response = requests.post( f"{self.base_url}/api/pull", json={"model": model, "stream": False}, From 5e4a792d5ceea82a78b8ca67d2f91a0097f45c3e Mon Sep 17 00:00:00 2001 From: Amadou Wolfgang Cisse Date: Tue, 3 Mar 2026 17:24:18 +0100 Subject: [PATCH 5/5] Resolve merge --- pyproject.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ae3bfb5..62227e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,11 +63,6 @@ rag = [ "langchain-community", "sentence-transformers", ] -graph = [ - "neo4j", - "llama-index-graph-stores-neo4j", - "langchain-community", -] [tool.setuptools] include-package-data = true