←back to Blog

Building a Secure and Memory-Enabled Cipher Workflow for AI Agents with Dynamic LLM Selection and API Integration

«`html

Building a Secure and Memory-Enabled Cipher Workflow for AI Agents with Dynamic LLM Selection and API Integration

This tutorial provides a step-by-step guide to creating a compact yet fully functional Cipher-based workflow. We will first address the secure capture of the Gemini API key in the Colab UI without exposing it in the code. We will then implement a dynamic LLM selection function that automatically switches between OpenAI, Gemini, or Anthropic based on the available API key. The setup phase ensures that Node.js and the Cipher CLI are installed. Afterward, we programmatically generate a cipher.yml configuration to enable a memory agent with long-term recall. We will also create helper functions to execute Cipher commands directly from Python and store key project decisions as persistent memories, which can be retrieved on demand.

Step 1: Securing API Key Input

We securely capture our Gemini API key using the getpass module, ensuring it remains hidden in the Colab UI:

import os, getpass
os.environ["GEMINI_API_KEY"] = getpass.getpass("Enter your Gemini API key: ").strip()

Step 2: Dynamic LLM Selection

The choose_llm function checks our environment variables and automatically selects the appropriate LLM provider, model, and API key based on what is available:

def choose_llm():
   if os.getenv("OPENAI_API_KEY"):
       return "openai", "gpt-4o-mini", "OPENAI_API_KEY"
   if os.getenv("GEMINI_API_KEY"):
       return "gemini", "gemini-2.5-flash", "GEMINI_API_KEY"
   if os.getenv("ANTHROPIC_API_KEY"):
       return "anthropic", "claude-3-5-haiku-20241022", "ANTHROPIC_API_KEY"
   raise RuntimeError("Set one API key before running.")

Step 3: Helper Function for Command Execution

We implement the run function that executes shell commands, providing visibility into both standard output and error:

def run(cmd, check=True, env=None):
   print("▸", cmd)
   p = subprocess.run(cmd, shell=True, text=True, capture_output=True, env=env)
   if p.stdout: print(p.stdout)
   if p.stderr: print(p.stderr)
   if check and p.returncode != 0:
       raise RuntimeError(f"Command failed: {cmd}")
   return p

Step 4: Ensuring Environment Setup

We define ensure_node_and_cipher to install Node.js, npm, and the Cipher CLI globally:

def ensure_node_and_cipher():
   run("sudo apt-get update -y && sudo apt-get install -y nodejs npm", check=False)
   run("npm install -g @byterover/cipher")

Step 5: Generating Configuration File

The write_cipher_yml function creates a cipher.yml configuration file for the memory agent:

def write_cipher_yml(workdir, provider, model, key_env):
   cfg = """
llm:
 provider: {provider}
 model: {model}
 apiKey: ${key_env}
systemPrompt:
 enabled: true
 content: |
   You are an AI programming assistant with long-term memory of prior decisions.
embedding:
 disabled: true
mcpServers:
 filesystem:
   type: stdio
   command: npx
   args: ['-y','@modelcontextprotocol/server-filesystem','.']
""".format(provider=provider, model=model, key_env=key_env)

   (workdir / "memAgent").mkdir(parents=True, exist_ok=True)
   (workdir / "memAgent" / "cipher.yml").write_text(cfg.strip() + "\n")

Step 6: Executing Cipher Commands

We create the cipher_once function to run a single Cipher CLI command:

def cipher_once(text, env=None, cwd=None):
   cmd = f'cipher {shlex.quote(text)}'
   p = subprocess.run(cmd, shell=True, text=True, capture_output=True, env=env, cwd=cwd)
   print("Cipher says:\n", p.stdout or p.stderr)
   return p.stdout.strip() or p.stderr.strip()

Step 7: Starting the API Server

The start_api function initializes the Cipher in API mode:

def start_api(env, cwd):
   proc = subprocess.Popen("cipher --mode api", shell=True, env=env, cwd=cwd,
                           stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
   for _ in range(30):
       try:
           r = requests.get("http://127.0.0.1:3000/health", timeout=2)
           if r.ok:
               print("API /health:", r.text)
               break
       except: pass
       time.sleep(1)
   return proc

Step 8: Main Function Execution

The main function orchestrates all components:

def main():
   provider, model, key_env = choose_llm()
   ensure_node_and_cipher()
   workdir = pathlib.Path(tempfile.mkdtemp(prefix="cipher_demo_"))
   write_cipher_yml(workdir, provider, model, key_env)
   env = os.environ.copy()

   cipher_once("Store decision: use pydantic for config validation; pytest fixtures for testing.", env, str(workdir))
   cipher_once("Remember: follow conventional commits; enforce black + isort in CI.", env, str(workdir))

   cipher_once("What did we standardize for config validation and Python formatting?", env, str(workdir))

   api_proc = start_api(env, str(workdir))
   time.sleep(3)
   api_proc.terminate()

if __name__ == "__main__":
   main()

Conclusion

This implementation creates a working Cipher environment that securely manages API keys, selects the right LLM provider automatically, and configures a memory-enabled agent through Python automation. The workflow encompasses decision logging, memory retrieval, and a live API endpoint, making it suitable for AI-assisted development pipelines.

For further resources, please check our GitHub Page for tutorials, codes, and notebooks. You can also follow us on Twitter and join our ML SubReddit for more community-driven discussions.

Star us on GitHub

Join our ML Subreddit

Sponsor us

«`