Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.mypy_cache
.ruff_cache
.DS_Store
__pycache__
310 changes: 310 additions & 0 deletions cookbook/dspy/dspy.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "245e75ab",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Collecting inferedge_moss\n",
" Using cached inferedge_moss-1.0.0b12-py3-none-any.whl.metadata (8.6 kB)\n",
"Requirement already satisfied: transformers>=4.21.0 in /opt/homebrew/lib/python3.11/site-packages (from inferedge_moss) (4.57.3)\n",
"Requirement already satisfied: numpy>=1.26.4 in /opt/homebrew/lib/python3.11/site-packages (from inferedge_moss) (1.26.4)\n",
"Requirement already satisfied: typing-extensions>=4.0.0 in /Users/keshav/Library/Python/3.11/lib/python/site-packages (from inferedge_moss) (4.15.0)\n",
"Requirement already satisfied: httpx>=0.25.0 in /opt/homebrew/lib/python3.11/site-packages (from inferedge_moss) (0.28.1)\n",
"Requirement already satisfied: onnxruntime>=1.12.0 in /opt/homebrew/lib/python3.11/site-packages (from inferedge_moss) (1.23.2)\n",
"Collecting inferedge-moss-core==0.3.0 (from inferedge_moss)\n",
" Using cached inferedge_moss_core-0.3.0-cp311-cp311-macosx_11_0_arm64.whl.metadata (2.2 kB)\n",
"Requirement already satisfied: anyio in /opt/homebrew/lib/python3.11/site-packages (from httpx>=0.25.0->inferedge_moss) (4.11.0)\n",
"Requirement already satisfied: certifi in /opt/homebrew/lib/python3.11/site-packages (from httpx>=0.25.0->inferedge_moss) (2025.10.5)\n",
"Requirement already satisfied: httpcore==1.* in /opt/homebrew/lib/python3.11/site-packages (from httpx>=0.25.0->inferedge_moss) (1.0.9)\n",
"Requirement already satisfied: idna in /opt/homebrew/lib/python3.11/site-packages (from httpx>=0.25.0->inferedge_moss) (3.11)\n",
"Requirement already satisfied: h11>=0.16 in /opt/homebrew/lib/python3.11/site-packages (from httpcore==1.*->httpx>=0.25.0->inferedge_moss) (0.16.0)\n",
"Requirement already satisfied: coloredlogs in /opt/homebrew/lib/python3.11/site-packages (from onnxruntime>=1.12.0->inferedge_moss) (15.0.1)\n",
"Requirement already satisfied: flatbuffers in /opt/homebrew/lib/python3.11/site-packages (from onnxruntime>=1.12.0->inferedge_moss) (25.12.19)\n",
"Requirement already satisfied: packaging in /Users/keshav/Library/Python/3.11/lib/python/site-packages (from onnxruntime>=1.12.0->inferedge_moss) (25.0)\n",
"Requirement already satisfied: protobuf in /opt/homebrew/lib/python3.11/site-packages (from onnxruntime>=1.12.0->inferedge_moss) (6.33.2)\n",
"Requirement already satisfied: sympy in /opt/homebrew/lib/python3.11/site-packages (from onnxruntime>=1.12.0->inferedge_moss) (1.14.0)\n",
"Requirement already satisfied: filelock in /opt/homebrew/lib/python3.11/site-packages (from transformers>=4.21.0->inferedge_moss) (3.20.1)\n",
"Requirement already satisfied: huggingface-hub<1.0,>=0.34.0 in /opt/homebrew/lib/python3.11/site-packages (from transformers>=4.21.0->inferedge_moss) (0.36.0)\n",
"Requirement already satisfied: pyyaml>=5.1 in /opt/homebrew/lib/python3.11/site-packages (from transformers>=4.21.0->inferedge_moss) (6.0.3)\n",
"Requirement already satisfied: regex!=2019.12.17 in /opt/homebrew/lib/python3.11/site-packages (from transformers>=4.21.0->inferedge_moss) (2025.11.3)\n",
"Requirement already satisfied: requests in /opt/homebrew/lib/python3.11/site-packages (from transformers>=4.21.0->inferedge_moss) (2.32.5)\n",
"Requirement already satisfied: tokenizers<=0.23.0,>=0.22.0 in /opt/homebrew/lib/python3.11/site-packages (from transformers>=4.21.0->inferedge_moss) (0.22.1)\n",
"Requirement already satisfied: safetensors>=0.4.3 in /opt/homebrew/lib/python3.11/site-packages (from transformers>=4.21.0->inferedge_moss) (0.7.0)\n",
"Requirement already satisfied: tqdm>=4.27 in /opt/homebrew/lib/python3.11/site-packages (from transformers>=4.21.0->inferedge_moss) (4.67.1)\n",
"Requirement already satisfied: fsspec>=2023.5.0 in /opt/homebrew/lib/python3.11/site-packages (from huggingface-hub<1.0,>=0.34.0->transformers>=4.21.0->inferedge_moss) (2025.9.0)\n",
"Requirement already satisfied: hf-xet<2.0.0,>=1.1.3 in /opt/homebrew/lib/python3.11/site-packages (from huggingface-hub<1.0,>=0.34.0->transformers>=4.21.0->inferedge_moss) (1.2.0)\n",
"Requirement already satisfied: sniffio>=1.1 in /opt/homebrew/lib/python3.11/site-packages (from anyio->httpx>=0.25.0->inferedge_moss) (1.3.1)\n",
"Requirement already satisfied: humanfriendly>=9.1 in /opt/homebrew/lib/python3.11/site-packages (from coloredlogs->onnxruntime>=1.12.0->inferedge_moss) (10.0)\n",
"Requirement already satisfied: charset_normalizer<4,>=2 in /opt/homebrew/lib/python3.11/site-packages (from requests->transformers>=4.21.0->inferedge_moss) (3.4.4)\n",
"Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/homebrew/lib/python3.11/site-packages (from requests->transformers>=4.21.0->inferedge_moss) (2.6.2)\n",
"Requirement already satisfied: mpmath<1.4,>=1.1.0 in /opt/homebrew/lib/python3.11/site-packages (from sympy->onnxruntime>=1.12.0->inferedge_moss) (1.3.0)\n",
"Using cached inferedge_moss-1.0.0b12-py3-none-any.whl (21 kB)\n",
"Using cached inferedge_moss_core-0.3.0-cp311-cp311-macosx_11_0_arm64.whl (733 kB)\n",
"Installing collected packages: inferedge-moss-core, inferedge_moss\n",
"\u001b[2K Attempting uninstall: inferedge-moss-core\n",
"\u001b[2K Found existing installation: inferedge-moss-core 0.2.3\n",
"\u001b[2K Uninstalling inferedge-moss-core-0.2.3:\n",
"\u001b[2K Successfully uninstalled inferedge-moss-core-0.2.32m0/2\u001b[0m [inferedge-moss-core]\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2/2\u001b[0m [inferedge_moss]]\n",
"\u001b[1A\u001b[2KSuccessfully installed inferedge-moss-core-0.3.0 inferedge_moss-1.0.0b12\n",
"\n",
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.3\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0\u001b[0m\n",
"\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49m/opt/homebrew/opt/python@3.11/bin/python3.11 -m pip install --upgrade pip\u001b[0m\n",
"Note: you may need to restart the kernel to use updated packages.\n"
]
}
],
"source": [
"pip install inferedge_moss dspy"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "2afd4122",
"metadata": {},
"outputs": [],
"source": [
"import asyncio\n",
"import dspy\n",
"from dspy.dsp.utils import dotdict\n",
"from dspy.primitives.prediction import Prediction\n",
"from dspy.retrievers.retrieve import Retrieve\n",
"from inferedge_moss import DocumentInfo, MossClient, QueryOptions\n",
"import nest_asyncio\n",
"import os\n",
"from dotenv import load_dotenv\n",
"\n",
"load_dotenv()\n",
"nest_asyncio.apply()\n",
"\n",
"def run_async(coro):\n",
" \"\"\"Helper to run async coroutines in a notebook environment.\"\"\"\n",
" try:\n",
" loop = asyncio.get_event_loop()\n",
" except RuntimeError:\n",
" loop = asyncio.new_event_loop()\n",
" asyncio.set_event_loop(loop)\n",
" \n",
" if loop.is_running():\n",
" return loop.run_until_complete(coro)\n",
" else:\n",
" return asyncio.run(coro)\n",
"\n",
"class MossRM(Retrieve):\n",
" \"\"\"A retrieval module that uses Moss (InferEdge) to return the top passages for a given query.\n",
"\n",
" Args:\n",
" index_name (str): The name of the Moss index.\n",
" moss_client (MossClient): An instance of the Moss client.\n",
" k (int, optional): The default number of top passages to retrieve. Default to 3.\n",
"\n",
" Examples:\n",
" Below is a code snippet that shows how to use Moss as the default retriever:\n",
" ```python\n",
" from inferedge_moss import MossClient\n",
" import dspy\n",
"\n",
" moss_client = MossClient(\"your-project-id\", \"your-project-key\")\n",
" retriever_model = MossRM(\"my_index_name\", moss_client=moss_client)\n",
" dspy.configure(rm=retriever_model)\n",
"\n",
" retrieve = dspy.Retrieve(k=1)\n",
" topK_passages = retrieve(\"what are the stages in planning, sanctioning and execution of public works\").passages\n",
" ```\n",
" \"\"\"\n",
"\n",
" def __init__(\n",
" self,\n",
" index_name: str,\n",
" moss_client: MossClient,\n",
" k: int = 3,\n",
" alpha: float = 0.5,\n",
" ):\n",
" self._index_name = index_name\n",
" self._moss_client = moss_client\n",
" self._alpha = alpha\n",
"\n",
" super().__init__(k=k)\n",
"\n",
" def forward(self, query_or_queries: str | list[str], k: int | None = None, **kwargs) -> Prediction:\n",
" \"\"\"Search with Moss for self.k top passages for query or queries.\n",
"\n",
" Args:\n",
" query_or_queries (Union[str, list[str]]): The query or queries to search for.\n",
" k (Optional[int]): The number of top passages to retrieve. Defaults to self.k.\n",
" kwargs : Additional arguments for Moss client.\n",
"\n",
" Returns:\n",
" dspy.Prediction: An object containing the retrieved passages.\n",
" \"\"\"\n",
" k = k if k is not None else self.k\n",
" queries = [query_or_queries] if isinstance(query_or_queries, str) else query_or_queries\n",
" queries = [q for q in queries if q]\n",
" passages = []\n",
"\n",
" for query in queries:\n",
" options = QueryOptions(top_k=k, alpha=self._alpha, **kwargs)\n",
" # Since MossClient methods are async, we use asyncio.run to call them synchronously.\n",
" # This assumes the loop is not already running, which is typical for DSPy RM calls.\n",
" result = asyncio.run(self._moss_client.query(self._index_name, query, options=options))\n",
"\n",
" for doc in result.docs:\n",
" passages.append(\n",
" dotdict({\"long_text\": doc.text, \"id\": doc.id, \"metadata\": doc.metadata, \"score\": doc.score})\n",
" )\n",
"\n",
" return passages\n",
"\n",
" def get_objects(self, num_samples: int = 5) -> list[dict]:\n",
" \"\"\"Get objects from Moss.\"\"\"\n",
" # Note: Moss's get_docs might return all docs or have limits.\n",
" # Here we attempt to fetch and return up to num_samples.\n",
" result = asyncio.run(self._moss_client.get_docs(self._index_name))\n",
" # result is likely a list of DocumentInfo or similar\n",
" objects = []\n",
" for i, doc in enumerate(result):\n",
" if i >= num_samples:\n",
" break\n",
" objects.append({\"id\": doc.id, \"text\": doc.text, \"metadata\": doc.metadata})\n",
" return objects\n",
"\n",
" def insert(self, new_object_properties: dict | list[dict]):\n",
" \"\"\"Insert one or more objects into Moss.\"\"\"\n",
" if isinstance(new_object_properties, dict):\n",
" new_object_properties = [new_object_properties]\n",
"\n",
" docs = [DocumentInfo(**props) for props in new_object_properties]\n",
" asyncio.run(self._moss_client.add_docs(self._index_name, docs))"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "71a53798",
"metadata": {},
"outputs": [],
"source": [
"import requests\n",
"import os\n",
"import asyncio\n",
"from inferedge_moss import MossClient, DocumentInfo\n",
"# 2. Initialize MossClient (Replace with your actual keys)\n",
"MOSS_PROJECT_ID = os.getenv(\"MOSS_PROJECT_ID\", os.getenv(\"MOSS_PROJECT_ID\"))\n",
"MOSS_PROJECT_KEY = os.getenv(\"MOSS_PROJECT_KEY\", os.getenv(\"MOSS_PROJECT_KEY\"))\n",
"\n",
"client = MossClient(MOSS_PROJECT_ID, MOSS_PROJECT_KEY)\n",
"INDEX_NAME = \"moss-cookbook\"\n",
"\n",
"llm = dspy.LM(model=\"gpt-4.1-mini\",api_key=\"sk-proj\")\n",
"# retriever_model = MossRM(INDEX_NAME, moss_client=client) ways to use Moss as retriever\n",
"# dspy.settings.configure(lm=llm, rm=retriever_model)\n",
"dspy.configure(lm=llm)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0f25a2e2",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/ny/7jk1wzqd7wz6z964crstxgqm0000gn/T/ipykernel_89530/729364508.py:22: RuntimeWarning: coroutine 'MossClient.load_index' was never awaited\n",
" client.load_index(INDEX)\n",
"RuntimeWarning: Enable tracemalloc to get the object allocation traceback\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Tool DEBUG] Found 10 results for: shipping address\n",
"[Tool DEBUG] First snippet: How can I change my shipping address? Contact our customer service team....\n",
"Answer: The shipping address is near St. Mark's Square.\n",
"\n",
"Tool calls made (Trajectory):\n",
"thought_0\n",
"tool_name_0\n",
"tool_args_0\n",
"observation_0\n",
"thought_1\n",
"tool_name_1\n",
"tool_args_1\n",
"observation_1\n"
]
}
],
"source": [
"# Create a ReAct agent using Moss as a tool\n",
"def moss_search(query: str):\n",
" \"\"\"Searches the Moss knowledge base for relevant passages.\"\"\"\n",
" \n",
" # 2. Configure query options\n",
" options = QueryOptions(top_k=TOP_K, alpha=0)\n",
" # 3. Use the run_async helper to call Moss from a sync function\n",
" # (Results are automatically returned as a list of documents)\n",
" results = run_async(client.query(INDEX, query, options=options))\n",
" # You can process multiple topK results as well\n",
" if results.docs:\n",
" print(f\"[Tool DEBUG] Found {len(results.docs)} results for: {query}\")\n",
" print(f\"[Tool DEBUG] First snippet: {results.docs[0].text}...\")\n",
" \n",
" if not results.docs:\n",
" return \"No relevant info found.\"\n",
" \n",
" return \"\\n\".join([f\"- {doc.text}\" for doc in results.docs])\n",
"\n",
"\n",
"INDEX = \"moss-cookbook\"\n",
"TOP_K = 10\n",
"client.load_index(INDEX)\n",
"\n",
"# Initialize the ReAct agent with the Moss search tool\n",
"react_agent = dspy.ReAct(\n",
" signature=\"question -> answer\",\n",
" tools=[moss_search],\n",
" max_iters=5\n",
")\n",
"\n",
"# Example usage\n",
"question = \"whats the shipping address?\"\n",
"result = react_agent(question=question)\n",
"\n",
"print(f\"Answer: {result.answer}\")\n",
"print(\"\\nTool calls made (Trajectory):\")\n",
"for step in result.trajectory:\n",
" print(step)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.11.14"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
74 changes: 74 additions & 0 deletions cookbook/langchain/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Moss LangChain Cookbook

This cookbook demonstrates how to integrate [Moss](https://moss.dev) with [LangChain](https://www.langchain.com/).

## Overview

Moss is a semantic search platform that allows you to build and query high-performance vector indices without managing infrastructure. This integration provides:

1. **MossRetriever**: A LangChain-compatible retriever for semantic search.
2. **MossSearchTool**: A tool for LangChain agents to search your knowledge base.

## Installation

Ensure you have the required packages installed:

```bash
pip install inferedge-moss langchain langchain-openai python-dotenv
```

## Setup

Create a `.env` file with your Moss credentials:

```env
MOSS_PROJECT_ID=your_project_id
MOSS_PROJECT_KEY=your_project_key
MOSS_INDEX_NAME=your_index_name
OPENAI_API_KEY=your_openai_api_key
```

## Usage

### Using the Retriever

The `MossRetriever` can be used in any LangChain chain.

```python
from moss_langchain import MossRetriever

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the plan here? are. we shipping this new package to pypi?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah folks at langchain told me to add this to pypi.

langchain-ai/langchain-community#514


retriever = MossRetriever(
project_id="your_id",
project_key="your_key",
index_name="your_index",
top_k=3,
alpha=0
)

docs = retriever.invoke("What is the refund policy?")
```

### Using the Agent Tool

You can also use Moss as a tool for an agent.

```python
from moss_langchain import get_moss_tool

tool = get_moss_tool(
project_id="your_id",
project_key="your_key",
index_name="your_index"
)

# Add to agent tools
tools = [tool]
```

## Examples

Check out the [moss_langchain.ipynb](moss_langchain.ipynb) notebook for complete examples including:

- Direct index querying
- Retrieval-Augmented Generation (RAG)
- ReAct Agent with Moss search
Loading