doctor

ELIZA chatbot, multi-turn

doctor/doctor.plumb
(* Dr Plumb — an ELIZA/DOCTOR chatbot.

   No API key required.  Run interactively:

     plumb-chat examples/doctor/doctor.plumb

   Or pipe input:

     echo '"I feel sad today"' | plumb examples/doctor/doctor.plumb
*)

let doctor : !string -> !string = agent {
  provider: "eliza",
  model: "doctor"
}

let main : !string -> !string = plumb(input, output) {
  input ; doctor ; output
}
doctor/doctor.py
"""Dr Plumb — interactive ELIZA chatbot.

No API key required. Uses the ELIZA provider built into the
plumbing runtime.

Usage:
    python doctor.py

Type messages at the prompt. Press Ctrl-D (EOF) to quit.
"""

import asyncio
import sys
from pathlib import Path

import plumbing as pb


async def main() -> None:
    spec = Path(__file__).parent / "doctor.plumb"
    async with await pb.run(spec, verbose=False) as pipeline:
        print("Dr Plumb is in. Type your thoughts (Ctrl-D to quit).\n")
        try:
            while True:
                line = await asyncio.get_event_loop().run_in_executor(
                    None, lambda: input("> ")
                )
                if not line.strip():
                    continue
                await pipeline.send(line.strip())
                response = await pipeline.recv()
                if response is None:
                    break
                print(f"\nDoctor: {response}\n")
        except EOFError:
            print()


if __name__ == "__main__":
    asyncio.run(main())
doctor/pydantic_agent.py
"""Dr Plumb via Pydantic AI — interactive ELIZA chatbot.

PlumbingModel is an interop adapter for the Pydantic AI ecosystem;
see doctor.py for the canonical bare API interface.

No API key required. Uses the ELIZA provider built into the
plumbing runtime.

Usage:
    pip install plumbing[pydantic]
    python pydantic_agent.py
"""

import asyncio
from pathlib import Path

from pydantic_ai import Agent

from persevere.plumbing.provider import PlumbingModel


async def main() -> None:
    spec = Path(__file__).parent / "doctor.plumb"

    # PlumbingModel maintains a stateful pipeline — the ELIZA agent
    # remembers prior turns without needing message_history.
    async with PlumbingModel(spec) as model:
        agent = Agent(model)
        print("Dr Plumb is in (Pydantic AI). Ctrl-D to quit.\n")
        history = None
        try:
            while True:
                line = await asyncio.get_event_loop().run_in_executor(
                    None, lambda: input("> ")
                )
                if not line.strip():
                    continue
                result = await agent.run(
                    line.strip(),
                    message_history=history,
                )
                history = result.all_messages()
                print(f"\nDoctor: {result.output}\n")
        except EOFError:
            print()


if __name__ == "__main__":
    asyncio.run(main())