Metadata-Version: 2.4
Name: prashflow
Version: 1.0.0
Summary: General-purpose AI application and agent runtime with chat, RAG, tools, MCP, memory, streaming and multi-provider LLM support.
Author: Prasanth
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pydantic>=2.7
Requires-Dist: pydantic-settings>=2.5
Requires-Dist: rich>=13.7
Requires-Dist: typer>=0.12
Requires-Dist: pyyaml>=6.0
Requires-Dist: requests>=2.32
Requires-Dist: sqlalchemy>=2.0
Requires-Dist: langchain-core>=0.3
Requires-Dist: langchain-community>=0.3
Requires-Dist: langchain-text-splitters>=0.3
Requires-Dist: langchain-chroma>=0.2
Requires-Dist: langgraph>=0.2
Requires-Dist: langchain-ollama>=0.2
Requires-Dist: chromadb>=0.5
Requires-Dist: rank-bm25>=0.2.2
Requires-Dist: pypdf>=5.0
Requires-Dist: docx2txt>=0.8
Requires-Dist: beautifulsoup4>=4.12
Requires-Dist: numpy>=1.26
Provides-Extra: openai
Requires-Dist: langchain-openai>=0.3; extra == "openai"
Provides-Extra: web
Requires-Dist: ddgs>=9.0; extra == "web"
Provides-Extra: rerank
Requires-Dist: sentence-transformers>=3.0; extra == "rerank"
Provides-Extra: qdrant
Requires-Dist: qdrant-client>=1.12; extra == "qdrant"
Requires-Dist: langchain-qdrant>=0.2; extra == "qdrant"
Provides-Extra: faiss
Requires-Dist: faiss-cpu>=1.9; extra == "faiss"
Provides-Extra: pgvector
Requires-Dist: pgvector>=0.3; extra == "pgvector"
Requires-Dist: psycopg[binary]>=3.2; extra == "pgvector"
Requires-Dist: langchain-postgres>=0.0.12; extra == "pgvector"
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == "mcp"
Provides-Extra: litellm
Requires-Dist: litellm>=1.70; extra == "litellm"
Provides-Extra: mysql
Requires-Dist: pymysql>=1.1; extra == "mysql"
Provides-Extra: postgres
Requires-Dist: psycopg[binary]>=3.2; extra == "postgres"
Provides-Extra: all
Requires-Dist: langchain-openai>=0.3; extra == "all"
Requires-Dist: ddgs>=9.0; extra == "all"
Requires-Dist: sentence-transformers>=3.0; extra == "all"
Requires-Dist: qdrant-client>=1.12; extra == "all"
Requires-Dist: langchain-qdrant>=0.2; extra == "all"
Requires-Dist: faiss-cpu>=1.9; extra == "all"
Requires-Dist: pgvector>=0.3; extra == "all"
Requires-Dist: psycopg[binary]>=3.2; extra == "all"
Requires-Dist: langchain-postgres>=0.0.12; extra == "all"
Requires-Dist: mcp>=1.0; extra == "all"
Requires-Dist: pymysql>=1.1; extra == "all"
Requires-Dist: litellm>=1.70; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: ruff>=0.8; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=6.0; extra == "dev"

# PrashFlow

**PrashFlow** is a general-purpose Python AI application and agent runtime designed to make Chat, RAG, agents, multi-agent systems, tools, MCP, memory, streaming, and multiple model providers available through a small Python API.

It is **not only a RAG library**.

## What you can build

- Local Ollama RAG applications
- Streaming chat with user sessions
- Semantic, BM25/keyword, hybrid, MMR and multi-query retrieval
- Chroma, Qdrant, FAISS, PGVector and in-memory vector stores
- PDF/DOCX/TXT/Markdown/CSV/web ingestion
- SQL database ingestion
- Single tool-using agents with LangGraph
- Agentic Ollama chat
- Supervisor, sequential and parallel multi-agent workflows
- Custom Python tools
- Optional web search
- MCP configuration/adapter boundary
- OpenAI and OpenAI-compatible models
- LiteLLM model gateway
- Environment-variable based YAML configuration
- Retries and clear validation errors

## Architecture

```text
                         PRASHFLOW
                             |
       +---------------------+----------------------+
       |                     |                      |
      Chat                   RAG                  Agent
       |                     |                      |
   Streaming           Ingestion/Retrieval      Tools
   Sessions                  |                    MCP
       |              +-------+-------+             |
       |              |       |       |             |
       |           Semantic  BM25   MMR             |
       |              |       |       |             |
       |              +-------+-------+             |
       |                      |                     |
       |                     RRF                    |
       |                      |                     |
       |                   Rerank                   |
       |                      |                     |
       +----------------------+---------------------+
                              |
                         Model Layer
                              |
                    +---------+---------+
                    |         |         |
                  Ollama    OpenAI    LiteLLM
                              |
                         MultiAgent
                              |
                 +------------+------------+
                 |            |            |
             Supervisor   Sequential    Parallel
```

## Installation

Basic:

```bash
pip install prashflow
```

All optional integrations:

```bash
pip install "prashflow[all]"
```

For local Ollama + Chroma RAG, the `all` extra is convenient. You can also install only the extras you need.

## 1. Local Ollama RAG in a few lines

Put documents in `./knowledge`:

```text
my-app/
├── knowledge/
│   ├── deployment.pdf
│   ├── architecture.docx
│   ├── troubleshooting.txt
│   └── security.md
├── data/
└── app.py
```

Pull local models:

```bash
ollama pull qwen3:8b
ollama pull nomic-embed-text
```

Python:

```python
from prashflow import RAG

rag = RAG(
    llm="ollama:qwen3:8b",
    embeddings="ollama:nomic-embed-text",
    vector_db="chroma:./data/chroma",
    retrieval={
        "type": "hybrid",
        "top_k": 5,
        "candidate_k": 20,
        "semantic_weight": 0.6,
        "keyword_weight": 0.4,
    },
)

rag.ingest("./knowledge")

print(rag.ask("What is our production deployment process?"))
```

PrashFlow hides the LangChain, Chroma, loader and embedding implementation from the application code.

## 2. Search algorithms

Semantic:

```python
rag.search("production deployment", search_type="semantic")
```

BM25/keyword:

```python
rag.search("JIRA-12345", search_type="keyword")
```

MMR:

```python
rag.search("deployment architecture", search_type="mmr")
```

Hybrid:

```python
rag.search("production deployment", search_type="hybrid")
```

Multi-query:

```python
rag.search("How do we release an application?", search_type="multi_query")
```

Hybrid combines semantic and keyword rankings with reciprocal-rank fusion. MMR adds diversity. An optional cross-encoder reranker can be enabled with the `rerank` extra.

## 3. Persistent Chroma

```python
rag = RAG(
    llm="ollama:qwen3:8b",
    embeddings="ollama:nomic-embed-text",
    vector_db={
        "provider": "chroma",
        "path": "./data/chroma",
        "collection": "company_docs",
    },
)
```

The Chroma data remains on disk after the Python process exits.

### In-memory vector store

```python
rag = RAG(
    llm="ollama:qwen3:8b",
    embeddings="ollama:nomic-embed-text",
    vector_db={"provider": "memory"},
)
```

This is intended for tests, demos and short-lived applications.

## 4. User session + streaming RAG chat

This is the recommended API for a local RAG chatbot:

```python
from prashflow import RAG

rag = RAG(
    llm="ollama:qwen3:8b",
    embeddings="ollama:nomic-embed-text",
    vector_db="chroma:./data/chroma",
    retrieval={"type": "hybrid", "top_k": 5},
)

rag.ingest("./knowledge")

session_id = "user-001"

while True:
    question = input("You: ")
    if question.lower() in {"exit", "quit"}:
        break

    print("AI: ", end="")
    for token in rag.chat_stream(
        session_id=session_id,
        query=question,
        search_type="hybrid",
    ):
        print(token, end="", flush=True)
    print()
```

The session stores conversation history independently for each `session_id`.

```text
user-001 -> conversation A
user-002 -> conversation B
user-003 -> conversation C
```

The default session backend is in-memory. A persistent Redis/PostgreSQL session backend can be added behind the same `SessionStore` abstraction.

## 5. Normal Chat

```python
from prashflow import Chat

chat = Chat(
    llm="ollama:qwen3:8b",
    session_id="user-001",
)

print(chat.chat("My name is Prash."))
print(chat.chat("What is my name?"))
```

Streaming:

```python
for token in chat.stream("Explain Kubernetes"):
    print(token, end="", flush=True)
```

## 6. Agentic Ollama Chat

Use `AgentChat` when the model should decide when to call tools.

```python
from prashflow import AgentChat

agent = AgentChat(
    llm="ollama:qwen3:8b",
    tools=["calculator"],
    session_id="user-001",
)

for token in agent.stream("Calculate 25% of 8000"):
    print(token, end="", flush=True)
```

PrashFlow uses LangGraph internally for the agent loop. The application does not need to build `StateGraph` or `ToolNode` itself.

## 7. Custom Python tools

```python
from prashflow import Agent
def get_server_status(server: str) -> str:
    """Get Linux server status."""
    return f"{server}: UP"
agent = Agent(
    llm={
        "provider": "ollama",
        "model": "qwen3:8b",
        "base_url": "http://localhost:11434",
    },
    tools=[
        "calculator",
        get_server_status,
    ],
)
print(
    agent.run(
        "Check web01 status and calculate 20 percent of 500."
    )
)
```

Tools can also require interactive approval:

```python
@tool(requires_approval=True, max_retries=2)
def restart_service(server: str, service: str) -> str:
    """Restart a Linux service."""
    # implement the real operation here
    return f"Restarted {service} on {server}"
```

## 8. Multi-agent

### Supervisor

```python
from prashflow import MultiAgent

team = MultiAgent(
    model="ollama:qwen3:8b",
    mode="supervisor",
    agents=[
        {
            "name": "researcher",
            "description": "Research technical information.",
            "tools": ["web_search"],
        },
        {
            "name": "calculator",
            "description": "Perform arithmetic calculations.",
            "tools": ["calculator"],
        },
    ],
)

print(team.run("Calculate 20% of 8000"))
```

The supervisor chooses the specialist.

### Sequential

```python
team = MultiAgent(
    model="ollama:qwen3:8b",
    mode="sequential",
    agents=[
        {"name": "planner", "description": "Create a plan."},
        {"name": "developer", "description": "Develop the solution."},
        {"name": "reviewer", "description": "Review the solution."},
    ],
)
```

Flow:

```text
Planner -> Developer -> Reviewer -> Final
```

### Parallel

```python
team = MultiAgent(
    model="ollama:qwen3:8b",
    mode="parallel",
    agents=[
        {"name": "security", "description": "Analyze security."},
        {"name": "performance", "description": "Analyze performance."},
        {"name": "architecture", "description": "Analyze architecture."},
    ],
)
```

The current reference implementation runs the specialist calls independently and synthesizes their results. An async concurrent implementation can be added for high-throughput production workloads.

### Streaming multi-agent

```python
for token in team.stream("Analyze this deployment"):
    print(token, end="", flush=True)
```

### Per-agent models

```python
team = MultiAgent(
    model="ollama:qwen3:8b",
    agents=[
        {
            "name": "researcher",
            "model": "ollama:qwen3:8b",
            "description": "Research information.",
        },
        {
            "name": "coder",
            "model": "ollama:qwen2.5-coder:14b",
            "description": "Write and review code.",
        },
    ],
)
```

## 9. RAG + Agent

```python
from prashflow import AgentChat, RAG

rag = RAG(
    llm="ollama:qwen3:8b",
    embeddings="ollama:nomic-embed-text",
    vector_db="chroma:./data/chroma",
    retrieval={"type": "hybrid", "top_k": 5},
)
rag.ingest("./knowledge")

agent = AgentChat(
    llm="ollama:qwen3:8b",
    tools=[rag.as_tool(), "calculator"],
)

print(agent.run("Find our production deployment procedure."))
```

## 10. Multi-agent + RAG

```python
team = MultiAgent(
    model="ollama:qwen3:8b",
    mode="supervisor",
    agents=[
        {
            "name": "company_knowledge",
            "description": "Answer questions from company documents.",
            "tools": [rag.as_tool()],
        },
        {
            "name": "calculator",
            "description": "Perform calculations.",
            "tools": ["calculator"],
        },
    ],
)
```

## 11. SQL ingestion

```python
rag.ingest_sql(
    url="postgresql+psycopg://user:password@localhost:5432/company",
    query="SELECT id, title, description FROM incidents",
    content_columns=["title", "description"],
    metadata_columns=["id"],
)
```

MySQL is supported through the SQLAlchemy connection URL when the MySQL extra is installed.

## 12. MCP

PrashFlow provides an MCP configuration boundary so MCP can be attached to agents without changing the agent API.

```python
agent = AgentChat(
    llm="ollama:qwen3:8b",
    mcp_servers=[
        {
            "name": "filesystem",
            "transport": "stdio",
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-filesystem", "./workspace"],
        }
    ],
)
```

The reference package validates MCP server configuration. For production MCP transport/session discovery, pin and implement against the MCP SDK version used by your organization; MCP SDK transport APIs can evolve.

## 13. LiteLLM

```python
from prashflow import Chat

chat = Chat(
    llm={
        "provider": "litellm",
        "model": "openai/gpt-4.1",
    }
)
```

The application API remains `chat.chat()` / `chat.stream()` while the provider is selected by LiteLLM.

## 14. OpenAI / OpenAI-compatible

```python
from prashflow import Chat

chat = Chat(
    llm={
        "provider": "openai-compatible",
        "model": "my-model",
        "base_url": "http://localhost:8000/v1",
        "api_key": "dummy",
    }
)
```

## 15. YAML configuration

`prashflow.yaml`:

```yaml
llm:
  provider: ollama
  model: qwen3:8b
  base_url: http://localhost:11434

embeddings:
  provider: ollama
  model: nomic-embed-text
  base_url: http://localhost:11434

vector_db:
  provider: chroma
  path: ./data/chroma
  collection: company_docs

retrieval:
  type: hybrid
  top_k: 5
  candidate_k: 20
  semantic_weight: 0.6
  keyword_weight: 0.4

chunking:
  size: 1000
  overlap: 200

reranker:
  enabled: false
```

Load it:

```python
from prashflow import RAG

rag = RAG.from_config("prashflow.yaml")
```

Environment variables are supported:

```yaml
llm:
  provider: openai
  model: ${OPENAI_MODEL}
  api_key: ${OPENAI_API_KEY}
```

## 16. Error handling

PrashFlow exposes typed exceptions:

```python
from prashflow import PrashFlowError

try:
    print(rag.ask("What is our deployment process?"))
except PrashFlowError as exc:
    print(f"PrashFlow error: {exc}")
```

Available categories include configuration, LLM, embedding, vector DB, document loading, retrieval, reranking, tool, agent and MCP errors.


## 17. Design philosophy

Application developers should write:

```python
from prashflow import RAG, AgentChat, MultiAgent
```

and should not need to directly assemble LangChain loaders, LangGraph state graphs, Chroma clients, BM25 indexes, tool nodes or model-provider adapters for common use cases.

Advanced developers can still customize the underlying components when needed.

## Roadmap

Planned production enhancements:

- true async/parallel specialist execution
- persistent Redis/PostgreSQL session backends
- complete MCP client/session discovery against a pinned SDK
- A2A support
- model fallback and cost routing
- observability/tracing
- structured output and Pydantic schemas
- FastAPI integration helpers
- ingestion manifests and changed-file detection
- background ingestion jobs
- citation objects with source/page metadata
