<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged bob</title>
<link>https://coderlegion.com/tag/bob</link>
<description>Powered by Question2Answer</description>
<item>
<title>Deep Agents in Action: Building a Multi-Agent Research System (published)</title>
<link>https://coderlegion.com/28616/deep-agents-in-action-building-a-multi-agent-research-system-published</link>
<description>&lt;p&gt;I tested yet another implementation of a multi-agent orchestrator.&lt;/p&gt;
&lt;p&gt;The paradigm for AI agents is shifting rapidly from single, monolithic prompt loops to structured multi-agent systems capable of division of labor, state persistence, and fine-grained tool orchestration.&lt;/p&gt;
&lt;p&gt;Although &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.ibm.com/products/watsonx-orchestrate&quot;&gt;IBM watsonx Orchestrate&lt;/a&gt; is my go-to solution for enterprise-level agent deployment, I wanted to experiment with LangChain's Deep Agents to evaluate its multi-agent orchestration capabilities.&lt;/p&gt;
&lt;h3 id=&quot;introduction-the-deep-agents-architecture&quot;&gt;Introduction: The Deep Agents Architecture&lt;/h3&gt;
&lt;p&gt;LangChain announced recently Managed Deep Agents, introducing a managed platform to execute deep agent topologies. The Deep Agents paradigm addresses the fundamental limitations of single-agent workflows—specifically context window degradation, tool confusion, and compounding reasoning failures during extended, multi-step tasks.&lt;/p&gt;
&lt;h3 id=&quot;what-is-the-deep-agents-pattern-&quot;&gt;What is the Deep Agents Pattern?&lt;/h3&gt;
&lt;p&gt;The Deep Agents pattern is a hierarchical multi-agent architecture where:&lt;/p&gt;
&lt;p&gt;A top-level Orchestrator decomposes complex tasks and delegates sub-tasks to specialists.&lt;br&gt;
Each Specialist Agent runs its own isolated ReAct loop with its own tools and memory.&lt;br&gt;
Specialists return structured results to the orchestrator, which synthesises them.&lt;br&gt;
Agents can be composed recursively — an orchestrator can itself be a sub-agent.&lt;br&gt;
This pattern adds genuine value over a single agent because:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;| Single Agent                       | Deep Agents                                       |
| ---------------------------------- | ------------------------------------------------- |
| One context window gets overloaded | Each agent has an isolated, focused context       |
| All reasoning in one prompt        | Specialised reasoning per domain                  |
| Hard to scale                      | Add specialists without changing the orchestrator |
| Hard to debug                      | Full delegation trace for auditability            |

---
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Excerpt from LangChain Deep Agents Documention;&lt;/strong&gt;&lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=5253859950868264376&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;deep-agents-overview&quot;&gt;Deep Agents overview&lt;/h3&gt;
&lt;p&gt;Deep Agents is the easiest way to start building agents and applications that are powered by LLMs—with built-in capabilities for file systems for context management, subagent-spawning, and long-term memory. Optional capabilities such as task planning and skills extend the harness when your use case needs them. You can use deep agents for any task, including complex, multi-step tasks.Deep Agents comes with the following capabilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Take actions in an environment&lt;/strong&gt;: Take actions via tools, read and&lt;br&gt;
write files, execute code &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect to your data&lt;/strong&gt;: Load memories,&lt;br&gt;
skills, and domain knowledge at the right moment &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manage growing context&lt;/strong&gt;: Summarize history and offload large results across long runs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parallelize tasks&lt;/strong&gt;: Delegate to general or specialized subagents running in isolated context windows &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stay in the loop&lt;/strong&gt;: Pause for human approval at critical decision points Improve over time: Update memory, skills, and prompts based on real usage&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;And from the Github repository;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Deep Agents is an open source agent harness — an opinionated agent that runs out of the box. Extend, override, or replace any piece.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Principles:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Opinionated&lt;/strong&gt; — defaults tuned for long-horizon, multi-step work&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extensible&lt;/strong&gt; — override or replace any piece without forking&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model-agnostic&lt;/strong&gt; — works with any LLM that supports tool calling: frontier, open-weight, or local &lt;/li&gt;
&lt;li&gt;Production-ready — built on LangGraph (streaming, persistence, checkpointing) with first-class tracing,&lt;br&gt;
evaluation, and deployment via LangSmith&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Features include:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sub-agents&lt;/strong&gt; — delegate tasks to agents with isolated context windows&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filesystem&lt;/strong&gt; — read, write, edit, or search over pluggable local,&lt;br&gt;
sandboxed, or remote backends&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context management&lt;/strong&gt; — summarize long threads and offload tool outputs&lt;br&gt;
to disk&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shell access&lt;/strong&gt; — run commands in your sandbox of choice&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistent memory&lt;/strong&gt; — pluggable state and store backends for&lt;br&gt;
cross-session recall&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Human-in-the-loop&lt;/strong&gt; — approve, edit, or reject tool calls before they&lt;br&gt;
run&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skills&lt;/strong&gt; — reusable behaviors the agent can load on demand&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tools&lt;/strong&gt; — bring your own functions or any MCP server&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;core-concepts-of-langchain-deep-agents&quot;&gt;Core Concepts of LangChain Deep Agents&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Hierarchical Orchestration: A primary agent acts as a supervisor,&lt;br&gt;
receiving high-level goals and delegating sub-tasks to dedicated&lt;br&gt;
specialist agents rather than executing every step sequentially in a&lt;br&gt;
single context loop.&lt;/li&gt;
&lt;li&gt;Specialized Agent Roles: Individual agents operate with tightly &lt;br&gt;
scoped system prompts, specialized tools, and isolated context &lt;br&gt;
windows to execute sub-tasks (e.g., information retrieval, numeric &lt;br&gt;
calculations, or document composition).&lt;/li&gt;
&lt;li&gt;Isolated Memory Stores: Each agent maintains its own short-term &lt;br&gt;
working memory, facts, and episodic memory, preventing state &lt;br&gt;
contamination across different phases of execution.&lt;/li&gt;
&lt;li&gt;ReAct Reasoning Loop: Sub-agents execute autonomous Thought → Action &lt;br&gt;
→ Observation loops using validated tool interfaces.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;technical-implementation&quot;&gt;Technical Implementation&lt;/h3&gt;
&lt;p&gt;Now that the feature set of &quot;Deep Agents&quot; is done, let's jump into a simple didatical (for myself) implementation.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The implementation in the provided repository (scaffolded via IBM Bob) provides a complete local and cloud-compatible implementation of the Deep Agents pattern.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;architecture-overview&quot;&gt;Architecture Overview&lt;/h3&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28616/deep-agents-in-action-building-a-multi-agent-research-system-published</guid>
<pubDate>Fri, 25 Sep 2026 14:39:24 +0000</pubDate>
</item>
<item>
<title>Kafka 101: Why Event Streaming is the Central Nervous System of Modern Data</title>
<link>https://coderlegion.com/15587/kafka-101-why-event-streaming-is-the-central-nervous-system-of-modern-data</link>
<description>&lt;p&gt;Digging into a new universe of data streaming after IBM’s recent accquisition of Confluent!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j0i0twnt306qpqbvp20g.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;
Data is no longer a static asset — it’s a live pulse. Following IBM’s $11B acquisition of Confluent, the bridge between real-time streaming and enterprise AI has never been shorter. I’m diving into the new universes of data-in-motion to see how this ‘Smart Data’ foundation is changing the game for hybrid cloud and agentic AI. &lt;/p&gt;
&lt;p&gt;In the traditional world of data, systems often talk to each other like a game of “telephone” — one service calls another, which calls another, creating a fragile web of dependencies. If one system goes down or slows down, the entire chain breaks. &lt;strong&gt;Event streaming&lt;/strong&gt; changes this paradigm by turning data into a continuous flow of events.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apache Kafka&lt;/strong&gt; acts as the “central nervous system” for this flow. Instead of direct connections, services publish events to a central cluster, and any interested service can “tune in” to listen. This decoupling allows businesses to react to customer actions in real-time — whether that’s processing a payment, updating a live dashboard, or triggering a delivery — without the systems ever needing to know each other exists.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Implementation: Building an E-commerce Stream&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjuyodzq1ift41st5iuj.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;Using two basic code samples I found on the net, one as a “producer” and one as a “consumer” to explain the concepts of Kafka, I asked Bob to build a simple comprehensive application.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simple Producer&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from confluent_kafka import Producer
import json

# Configuration for connecting to the Kafka cluster
config = {'bootstrap.servers': 'localhost:9092'}
producer = Producer(config)

def delivery_report(err, msg):
    if err is not None:
        print(f&quot;Message delivery failed: {err}&quot;)
    else:
        print(f&quot;Order sent to {msg.topic()} [{msg.partition()}]&quot;)

# Simulate an order
order_data = {
    &quot;order_id&quot;: 1001,
    &quot;user&quot;: &quot;jane_doe&quot;,
    &quot;total&quot;: 59.99,
    &quot;items&quot;: [&quot;Wireless Mouse&quot;, &quot;Keyboard&quot;]
}

# Trigger the send (Asynchronous)
producer.produce(
    'orders', 
    key=&quot;1001&quot;, 
    value=json.dumps(order_data), 
    callback=delivery_report
)

producer.flush() # Wait for any outstanding messages to be delivered

&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Simple Consumer&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from confluent_kafka import Consumer

config = {
    'bootstrap.servers': 'localhost:9092',
    'group.id': 'shipping-group', # Helps Kafka track which orders this group has seen
    'auto.offset.reset': 'earliest'
}

consumer = Consumer(config)
consumer.subscribe(['orders'])

try:
    while True:
        msg = consumer.poll(1.0) # Check for new messages every 1 second
        if msg is None: continue
        if msg.error():
            print(f&quot;Consumer error: {msg.error()}&quot;)
            continue

        print(f&quot;Received Order: {msg.value().decode('utf-8')}&quot;)
        print(&quot;Action: Initiating packaging and shipping...&quot;)
finally:
    consumer.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After reading my conceptual samples, Bob provided the application structure which follows.&lt;/p&gt;
&lt;h4 id=&quot;the-producer-capturing-the-event&quot;&gt;The Producer: Capturing the Event&lt;/h4&gt;
&lt;p&gt;The Producer (found in &lt;code&gt;src/producer.py&lt;/code&gt;) acts as the storefront. When an order is placed, it creates a JSON-serializable dictionary containing the order ID, user details, and items. It uses the &lt;code&gt;confluent-kafka&lt;/code&gt; library to &quot;produce&quot; this message to a topic called &lt;code&gt;orders&lt;/code&gt;, using the &lt;code&gt;order_id&lt;/code&gt; as a key to ensure all updates for a specific order land in the same partition. A delivery callback function is utilized to provide immediate feedback on whether the message successfully reached the Kafka broker or if a retry is necessary.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yexfofty4yn3i5zyc2zq.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;&quot;&quot;&quot;
Kafka Producer - E-commerce Order Processing System
Simulates an online store sending order events to Kafka
&quot;&quot;&quot;

from confluent_kafka import Producer
import json
import time
import random
from datetime import datetime

# Configuration for connecting to the Kafka cluster
config = {
    'bootstrap.servers': 'localhost:9092',
    'client.id': 'ecommerce-producer'
}

producer = Producer(config)

def delivery_report(err, msg):
    &quot;&quot;&quot;
    Callback function called once for each message produced to indicate delivery result.
    Triggered by poll() or flush().
    &quot;&quot;&quot;
    if err is not None:
        print(f&quot;❌ Message delivery failed: {err}&quot;)
    else:
        print(f&quot;✅ Order sent to topic '{msg.topic()}' [partition {msg.partition()}] at offset {msg.offset()}&quot;)

# Sample product catalog
PRODUCTS = [
    {&quot;name&quot;: &quot;Wireless Mouse&quot;, &quot;price&quot;: 29.99},
    {&quot;name&quot;: &quot;Mechanical Keyboard&quot;, &quot;price&quot;: 89.99},
    {&quot;name&quot;: &quot;USB-C Cable&quot;, &quot;price&quot;: 12.99},
    {&quot;name&quot;: &quot;Laptop Stand&quot;, &quot;price&quot;: 45.00},
    {&quot;name&quot;: &quot;Webcam HD&quot;, &quot;price&quot;: 79.99},
    {&quot;name&quot;: &quot;Headphones&quot;, &quot;price&quot;: 149.99},
    {&quot;name&quot;: &quot;Monitor 27\&quot;&quot;, &quot;price&quot;: 299.99},
    {&quot;name&quot;: &quot;Desk Lamp&quot;, &quot;price&quot;: 34.99}
]

USERS = [&quot;alice_smith&quot;, &quot;bob_jones&quot;, &quot;charlie_brown&quot;, &quot;diana_prince&quot;, &quot;eve_adams&quot;]

def generate_order(order_id):
    &quot;&quot;&quot;Generate a random order&quot;&quot;&quot;
    num_items = random.randint(1, 4)
    items = random.sample(PRODUCTS, num_items)
    
    order_data = {
        &quot;order_id&quot;: order_id,
        &quot;user&quot;: random.choice(USERS),
        &quot;timestamp&quot;: datetime.now().isoformat(),
        &quot;items&quot;: [item[&quot;name&quot;] for item in items],
        &quot;total&quot;: round(sum(item[&quot;price&quot;] for item in items), 2),
        &quot;status&quot;: &quot;pending&quot;
    }
    return order_data

def produce_orders(num_orders=10, delay=2):
    &quot;&quot;&quot;
    Produce multiple orders to Kafka
    
    Args:
        num_orders: Number of orders to generate
        delay: Delay in seconds between orders
    &quot;&quot;&quot;
    print(f&quot; Starting E-commerce Order Producer&quot;)
    print(f&quot; Will produce {num_orders} orders with {delay}s delay between each&quot;)
    print(f&quot; Connected to Kafka at: {config['bootstrap.servers']}&quot;)
    print(&quot;-&quot; * 70)
    
    for i in range(1, num_orders + 1):
        try:
            # Generate order
            order_data = generate_order(1000 + i)
            
            # Print order details
            print(f&quot;\n Order #{order_data['order_id']}:&quot;)
            print(f&quot;   User: {order_data['user']}&quot;)
            print(f&quot;   Items: {', '.join(order_data['items'])}&quot;)
            print(f&quot;   Total: ${order_data['total']}&quot;)
            
            # Produce message to Kafka
            producer.produce(
                topic='orders',
                key=str(order_data['order_id']),
                value=json.dumps(order_data),
                callback=delivery_report
            )
            
            # Trigger delivery reports by polling
            producer.poll(0)
            
            # Wait before next order
            if i &amp;lt; num_orders:
                time.sleep(delay)
                
        except KeyboardInterrupt:
            print(&quot;\n⚠️  Producer interrupted by user&quot;)
            break
        except Exception as e:
            print(f&quot;❌ Error producing message: {e}&quot;)
    
    # Wait for all messages to be delivered
    print(&quot;\n⏳ Flushing remaining messages...&quot;)
    producer.flush()
    print(&quot;✅ All orders sent successfully!&quot;)

if __name__ == &quot;__main__&quot;:
    import sys
    
    # Parse command line arguments
    num_orders = int(sys.argv[1]) if len(sys.argv) &amp;gt; 1 else 10
    delay = float(sys.argv[2]) if len(sys.argv) &amp;gt; 2 else 2
    
    try:
        produce_orders(num_orders, delay)
    except KeyboardInterrupt:
        print(&quot;\n Producer stopped&quot;)
    except Exception as e:
        print(f&quot;❌ Fatal error: {e}&quot;)

# Made with Bob
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-consumer-decoupled-processing&quot;&gt;The Consumer: Decoupled Processing&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/er5ozyspeu54dffbwckn.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
The Consumer logic (found in &lt;code&gt;src/consumer.py&lt;/code&gt;) demonstrates the power of Consumer Groups. Multiple independent services—Shipping, Email, and Analytics—all subscribe to the same orders topic simultaneously. Because each service belongs to its own group (e.g., &lt;code&gt;shipping-group&lt;/code&gt;), Kafka tracks their progress (offsets) individually. This means the Shipping department can process messages at its own pace without affecting the speed of the Email service or the Analytics dashboard.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;&quot;&quot;&quot;
Kafka Consumer - E-commerce Order Processing System
Simulates different departments (Shipping, Email, Analytics) consuming order events
&quot;&quot;&quot;

from confluent_kafka import Consumer, KafkaError
import json
import sys
from datetime import datetime

def create_consumer(group_id, bootstrap_servers='localhost:9092'):
    &quot;&quot;&quot;
    Create and configure a Kafka consumer
    
    Args:
        group_id: Consumer group identifier
        bootstrap_servers: Kafka broker addresses
    
    Returns:
        Configured Consumer instance
    &quot;&quot;&quot;
    config = {
        'bootstrap.servers': bootstrap_servers,
        'group.id': group_id,
        'auto.offset.reset': 'earliest',  # Start from beginning if no offset exists
        'enable.auto.commit': True,
        'auto.commit.interval.ms': 1000,
        'session.timeout.ms': 6000,
        'client.id': f'{group_id}-client'
    }
    
    return Consumer(config)

def process_shipping(order):
    &quot;&quot;&quot;Process order for shipping department&quot;&quot;&quot;
    print(f&quot;\n SHIPPING DEPARTMENT&quot;)
    print(f&quot;   Order ID: {order['order_id']}&quot;)
    print(f&quot;   Customer: {order['user']}&quot;)
    print(f&quot;   Items to pack: {', '.join(order['items'])}&quot;)
    print(f&quot;   ✅ Initiating packaging and shipping process...&quot;)

def process_email(order):
    &quot;&quot;&quot;Process order for email service&quot;&quot;&quot;
    print(f&quot;\n EMAIL SERVICE&quot;)
    print(f&quot;   Order ID: {order['order_id']}&quot;)
    print(f&quot;   Recipient: {order['user']}&quot;)
    print(f&quot;   Total: ${order['total']}&quot;)
    print(f&quot;   ✅ Sending order confirmation email...&quot;)

def process_analytics(order):
    &quot;&quot;&quot;Process order for analytics dashboard&quot;&quot;&quot;
    print(f&quot;\n ANALYTICS DASHBOARD&quot;)
    print(f&quot;   Order ID: {order['order_id']}&quot;)
    print(f&quot;   Revenue: ${order['total']}&quot;)
    print(f&quot;   Items count: {len(order['items'])}&quot;)
    print(f&quot;   Timestamp: {order['timestamp']}&quot;)
    print(f&quot;   ✅ Updating live sales dashboard...&quot;)

# Department processors mapping
PROCESSORS = {
    'shipping-group': process_shipping,
    'email-group': process_email,
    'analytics-group': process_analytics
}

def consume_orders(group_id, topic='orders'):
    &quot;&quot;&quot;
    Consume orders from Kafka topic
    
    Args:
        group_id: Consumer group (shipping-group, email-group, analytics-group)
        topic: Kafka topic to subscribe to
    &quot;&quot;&quot;
    consumer = create_consumer(group_id)
    consumer.subscribe([topic])
    
    processor = PROCESSORS.get(group_id, process_shipping)
    department = group_id.replace('-group', '').upper()
    
    print(f&quot; Starting {department} Consumer&quot;)
    print(f&quot; Consumer Group: {group_id}&quot;)
    print(f&quot; Subscribed to topic: {topic}&quot;)
    print(f&quot; Connected to Kafka at: localhost:9092&quot;)
    print(&quot;-&quot; * 70)
    print(&quot;⏳ Waiting for messages... (Press Ctrl+C to stop)\n&quot;)
    
    try:
        message_count = 0
        while True:
            # Poll for messages (timeout in seconds)
            msg = consumer.poll(timeout=1.0)
            
            if msg is None:
                continue
            
            if msg.error():
                if msg.error().code() == KafkaError._PARTITION_EOF:
                    # End of partition event
                    print(f&quot; Reached end of partition {msg.partition()}&quot;)
                else:
                    print(f&quot;❌ Consumer error: {msg.error()}&quot;)
                continue
            
            # Process the message
            try:
                message_count += 1
                order_data = json.loads(msg.value().decode('utf-8'))
                
                print(f&quot;\n{'='*70}&quot;)
                print(f&quot; Message #{message_count} received from partition {msg.partition()} at offset {msg.offset()}&quot;)
                
                # Call the appropriate processor
                processor(order_data)
                
                print(f&quot;{'='*70}&quot;)
                
            except json.JSONDecodeError as e:
                print(f&quot;❌ Failed to decode message: {e}&quot;)
            except Exception as e:
                print(f&quot;❌ Error processing message: {e}&quot;)
    
    except KeyboardInterrupt:
        print(f&quot;\n\n⚠️  Consumer interrupted by user&quot;)
        print(f&quot; Total messages processed: {message_count}&quot;)
    
    finally:
        # Close the consumer to commit final offsets
        print(&quot; Closing consumer...&quot;)
        consumer.close()
        print(&quot;✅ Consumer closed successfully&quot;)

if __name__ == &quot;__main__&quot;:
    # Parse command line arguments
    if len(sys.argv) &amp;lt; 2:
        print(&quot;Usage: python consumer.py &amp;lt;group_id&amp;gt; [topic]&quot;)
        print(&quot;\nAvailable consumer groups:&quot;)
        print(&quot;  - shipping-group   : Processes orders for shipping&quot;)
        print(&quot;  - email-group      : Sends confirmation emails&quot;)
        print(&quot;  - analytics-group  : Updates analytics dashboard&quot;)
        print(&quot;\nExample: python consumer.py shipping-group&quot;)
        sys.exit(1)
    
    group_id = sys.argv[1]
    topic = sys.argv[2] if len(sys.argv) &amp;gt; 2 else 'orders'
    
    if group_id not in PROCESSORS:
        print(f&quot;⚠️  Warning: Unknown group_id '{group_id}'. Using default processor.&quot;)
    
    try:
        consume_orders(group_id, topic)
    except Exception as e:
        print(f&quot;❌ Fatal error: {e}&quot;)
        sys.exit(1)

# Made with Bob
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-cluster-ensuring-reliability&quot;&gt;The Cluster: Ensuring Reliability&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxxi1qwyj7mhx3oezanl.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;The heart of the system is the &lt;strong&gt;Broker Kafka Cluster&lt;/strong&gt; (defined in &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;[Architecture.md](https://github.com/aairom/kafka-101/blob/main/Docs/Architecture.md)&lt;/code&gt;). By running three brokers coordinated by Zookeeper, the architecture ensures &lt;strong&gt;High Availability&lt;/strong&gt;. Each data partition is replicated across at least two brokers; if one broker fails, another automatically takes over as the &quot;leader,&quot; ensuring that no orders are lost and the system remains online. This fault tolerance is what makes Kafka &quot;production-ready&quot; compared to simple message queues.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;version: '3.8'

services:
  zookeeper:
    image: confluentinc/cp-zookeeper:7.5.0
    hostname: zookeeper
    container_name: zookeeper
    ports:
      - &quot;2181:2181&quot;
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    volumes:
      - zookeeper-data:/var/lib/zookeeper/data
      - zookeeper-logs:/var/lib/zookeeper/log

  kafka-broker-1:
    image: confluentinc/cp-kafka:7.5.0
    hostname: kafka-broker-1
    container_name: kafka-broker-1
    depends_on:
      - zookeeper
    ports:
      - &quot;9092:9092&quot;
      - &quot;19092:19092&quot;
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker-1:29092,PLAINTEXT_HOST://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
      KAFKA_JMX_PORT: 9101
      KAFKA_JMX_HOSTNAME: localhost
    volumes:
      - kafka-broker-1-data:/var/lib/kafka/data

  kafka-broker-2:
    image: confluentinc/cp-kafka:7.5.0
    hostname: kafka-broker-2
    container_name: kafka-broker-2
    depends_on:
      - zookeeper
    ports:
      - &quot;9093:9093&quot;
      - &quot;19093:19093&quot;
    environment:
      KAFKA_BROKER_ID: 2
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker-2:29093,PLAINTEXT_HOST://localhost:9093
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
      KAFKA_JMX_PORT: 9102
      KAFKA_JMX_HOSTNAME: localhost
    volumes:
      - kafka-broker-2-data:/var/lib/kafka/data

  kafka-broker-3:
    image: confluentinc/cp-kafka:7.5.0
    hostname: kafka-broker-3
    container_name: kafka-broker-3
    depends_on:
      - zookeeper
    ports:
      - &quot;9094:9094&quot;
      - &quot;19094:19094&quot;
    environment:
      KAFKA_BROKER_ID: 3
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka-broker-3:29094,PLAINTEXT_HOST://localhost:9094
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 2
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 2
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 2
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
      KAFKA_JMX_PORT: 9103
      KAFKA_JMX_HOSTNAME: localhost
    volumes:
      - kafka-broker-3-data:/var/lib/kafka/data

volumes:
  zookeeper-data:
  zookeeper-logs:
  kafka-broker-1-data:
  kafka-broker-2-data:
  kafka-broker-3-data:

# Made with Bob
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-monitor-real-time-observability&quot;&gt;The Monitor: Real-Time Observability&lt;/h4&gt;
&lt;p&gt;To manage this distributed flow, the Monitor Runner (&lt;code&gt;monitor_runner.py&lt;/code&gt;) provides a unified view of the entire ecosystem. It orchestrates the lifecycle of the producer and all consumer threads while capturing their standard output into a web-based dashboard. This allows developers to see the &quot;path of a message&quot; in real-time: from the moment the Producer sends Order #1001 to the moment all three consumer groups acknowledge and process it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;&quot;&quot;&quot;
Kafka Monitor Web Server
Provides a web interface to monitor producer and consumer outputs in real-time
&quot;&quot;&quot;

import asyncio
import json
import subprocess
import threading
from datetime import datetime
from pathlib import Path
from http.server import HTTPServer, SimpleHTTPRequestHandler
import socketserver

class TerminalMonitor:
    &quot;&quot;&quot;Monitor terminal outputs and store them&quot;&quot;&quot;
    
    def __init__(self):
        self.outputs = {
            'producer': [],
            'shipping': [],
            'email': [],
            'analytics': []
        }
        self.max_lines = 1000  # Keep last 1000 lines per terminal
        
    def add_output(self, terminal_id, line):
        &quot;&quot;&quot;Add a line to terminal output&quot;&quot;&quot;
        timestamp = datetime.now().strftime('%H:%M:%S')
        entry = {
            'timestamp': timestamp,
            'line': line
        }
        
        if terminal_id in self.outputs:
            self.outputs[terminal_id].append(entry)
            # Keep only last max_lines
            if len(self.outputs[terminal_id]) &amp;gt; self.max_lines:
                self.outputs[terminal_id] = self.outputs[terminal_id][-self.max_lines:]
    
    def get_outputs(self):
        &quot;&quot;&quot;Get all terminal outputs&quot;&quot;&quot;
        return self.outputs
    
    def clear_terminal(self, terminal_id):
        &quot;&quot;&quot;Clear a specific terminal&quot;&quot;&quot;
        if terminal_id in self.outputs:
            self.outputs[terminal_id] = []
    
    def clear_all(self):
        &quot;&quot;&quot;Clear all terminals&quot;&quot;&quot;
        for terminal_id in self.outputs:
            self.outputs[terminal_id] = []

# Global monitor instance
monitor = TerminalMonitor()

class MonitorRequestHandler(SimpleHTTPRequestHandler):
    &quot;&quot;&quot;Custom HTTP request handler&quot;&quot;&quot;
    
    def do_GET(self):
        &quot;&quot;&quot;Handle GET requests&quot;&quot;&quot;
        if self.path == '/':
            self.path = '/monitor.html'
        elif self.path == '/api/outputs':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            self.wfile.write(json.dumps(monitor.get
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15587/kafka-101-why-event-streaming-is-the-central-nervous-system-of-modern-data</guid>
<pubDate>Thu, 23 Apr 2026 05:55:23 +0000</pubDate>
</item>
<item>
<title>Image Generation with Ollama is back with Japanese, Korean and Chinese Languages  Support!</title>
<link>https://coderlegion.com/15586/image-generation-with-ollama-is-back-with-japanese-korean-and-chinese-languages-support</link>
<description>&lt;p&gt;An international collaborative work &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4hz45saspaz1xcdy2lzu.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;At the start of the year, I developed an image generation application using Bob to build a custom interface. The backend integrates the ‘&lt;strong&gt;x/flux2-klein&lt;/strong&gt;’ and ‘&lt;strong&gt;x/z-image-turbo&lt;/strong&gt;’ models alongside Ollama. Recently, the application received significant enhancements thanks to contributions from my colleague, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/oniak3/&quot;&gt;Akira Onishi&lt;/a&gt;-san, in Japan.&lt;/p&gt;
&lt;p&gt;What’s truly fantastic is the way my colleague transformed the app; by leveraging &lt;strong&gt;Bob&lt;/strong&gt;, he made the architecture modular and much more secure. He also went the extra mile by refining the &lt;strong&gt;bash scripts&lt;/strong&gt;, making the automatic startup and shutdown processes more robust than ever.&lt;/p&gt;
&lt;p&gt;So let’s dig into the application’s enhancements.&lt;/p&gt;
&lt;h3 id=&quot;implementation&quot;&gt;Implementation&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6izbv22k15wnc1ju2998.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;The architecture of the application remains the same, but the main enhanced modules are provided hereafter (the change log is accessible on GitHub)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;app.js&lt;/strong&gt;: The latest update to &lt;code&gt;app.js&lt;/code&gt; introduces significant hardening and performance tracking. On the security front, we’ve eliminated vulnerabilities by replacing &lt;code&gt;innerHTML&lt;/code&gt; with &lt;strong&gt;safe DOM&lt;/strong&gt; manipulation and implementing &lt;strong&gt;filename sanitization&lt;/strong&gt; to prevent directory traversal. Additionally, we’ve integrated &lt;strong&gt;memory usage tracking&lt;/strong&gt; within the history logs and refined the &lt;strong&gt;IME composition handling&lt;/strong&gt; to ensure a seamless experience for Japanese input.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Application state
let generationHistory = [];

// DOM elements
const promptInput = document.getElementById('prompt');
const modelSelect = document.getElementById('model');
const generateBtn = document.getElementById('generateBtn');
const resultContainer = document.getElementById('result');
const statusDiv = document.getElementById('status');
const historyList = document.getElementById('historyList');
const btnText = generateBtn.querySelector('.btn-text');
const loader = generateBtn.querySelector('.loader');

// Check Ollama connection on load
checkOllamaConnection();

// Event listeners
generateBtn.addEventListener('click', generateImage);

// Check if Ollama is running
async function checkOllamaConnection() {
    statusDiv.textContent = 'Checking connection...';
    statusDiv.className = 'status checking';
    
    try {
        const response = await fetch('/api/health');
        const data = await response.json();
        
        if (data.status === 'ok') {
            statusDiv.textContent = '✓ Connected to Ollama';
            statusDiv.className = 'status connected';
        } else {
            statusDiv.textContent = '✗ Ollama not connected';
            statusDiv.className = 'status disconnected';
        }
    } catch (error) {
        statusDiv.textContent = '✗ Cannot connect to server';
        statusDiv.className = 'status disconnected';
    }
}

// Generate image function
async function generateImage() {
    const prompt = promptInput.value.trim();
    const model = modelSelect.value;
    
    if (!prompt) {
        alert('Please enter a prompt');
        return;
    }
    
    // Disable button and show loading state
    generateBtn.disabled = true;
    btnText.textContent = 'Generating...';
    loader.style.display = 'block';
    
    // Clear previous result
    resultContainer.innerHTML = '';
    const placeholder = document.createElement('div');
    placeholder.className = 'placeholder';
    const p = document.createElement('p');
    p.textContent = 'Generating your image...';
    placeholder.appendChild(p);
    resultContainer.appendChild(placeholder);
    
    try {
        const response = await fetch('/api/generate', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ prompt, model })
        });
        
        const data = await response.json();
        
        if (data.success) {
            displayResult(data, prompt, model);
            addToHistory(prompt, model, data.result, data.memoryUsage);
        } else {
            displayError(data.error || 'Failed to generate image');
        }
    } catch (error) {
        displayError('Network error: ' + error.message);
    } finally {
        // Re-enable button
        generateBtn.disabled = false;
        btnText.textContent = 'Generate Image';
        loader.style.display = 'none';
    }
}

// Display the result
function displayResult(data, prompt, model) {
    const resultContent = document.createElement('div');
    resultContent.className = 'result-content';
    
    // Check if result contains image data (base64)
    if (data.result &amp;amp;&amp;amp; (data.result.startsWith('data:image') || data.result.includes('base64'))) {
        const img = document.createElement('img');
        img.src = data.result;
        img.alt = 'Generated image';
        img.className = 'result-image';
        resultContent.appendChild(img);
        
        // Add download button
        const downloadBtn = document.createElement('button');
        downloadBtn.className = 'download-btn';
        downloadBtn.textContent = '⬇️ Download Image';
        downloadBtn.onclick = () =&amp;gt; downloadImage(data.result, prompt);
        resultContent.appendChild(downloadBtn);
    } else {
        // Display as text if not an image
        const textDiv = document.createElement('div');
        textDiv.className = 'result-text';
        textDiv.textContent = data.result;
        resultContent.appendChild(textDiv);
    }
    
    // Add info section
    const infoDiv = document.createElement('div');
    infoDiv.className = 'result-info';
    
    const modelLabel = document.createElement('strong');
    modelLabel.textContent = 'Model:';
    infoDiv.appendChild(modelLabel);
    infoDiv.appendChild(document.createTextNode(' ' + model));
    infoDiv.appendChild(document.createElement('br'));
    
    const promptLabel = document.createElement('strong');
    promptLabel.textContent = 'Prompt:';
    infoDiv.appendChild(promptLabel);
    infoDiv.appendChild(document.createTextNode(' ' + prompt));
    
    resultContent.appendChild(infoDiv);
    
    resultContainer.innerHTML = '';
    resultContainer.appendChild(resultContent);
}

// Download image with custom filename
function downloadImage(imageDataUrl, defaultPrompt) {
    // Generate a default filename from the prompt (sanitized)
    const sanitizedPrompt = defaultPrompt
        .toLowerCase()
        .replace(/[^a-z0-9\s]/g, '')
        .replace(/\s+/g, '-')
        .substring(0, 50);
    
    const timestamp = new Date().toISOString().split('T')[0];
    const defaultFilename = `${sanitizedPrompt}-${timestamp}.png`;
    
    // Prompt user for filename
    const filename = prompt('Enter filename for the image:', defaultFilename);
    
    // If user cancels, don't download
    if (!filename) return;
    
    // Remove all path separators to prevent directory traversal
    const sanitizedFilename = filename.replace(/[^a-zA-Z0-9._-]/g, '_');
    
    // Ensure filename has .png extension
    const finalFilename = sanitizedFilename.endsWith('.png') ? sanitizedFilename : `${sanitizedFilename}.png`;
    
    // Create a temporary link element and trigger download
    const link = document.createElement('a');
    link.href = imageDataUrl;
    link.download = finalFilename;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
}

// Display error message
function displayError(message) {
    const errorDiv = document.createElement('div');
    errorDiv.className = 'error-message';
    
    const strong = document.createElement('strong');
    strong.textContent = 'Error:';
    
    const messageText = document.createTextNode(' ' + message);
    
    const br1 = document.createElement('br');
    const br2 = document.createElement('br');
    
    const small = document.createElement('small');
    small.textContent = 'Make sure Ollama is running and the models are installed.';
    
    errorDiv.appendChild(strong);
    errorDiv.appendChild(messageText);
    errorDiv.appendChild(br1);
    errorDiv.appendChild(br2);
    errorDiv.appendChild(small);
    
    resultContainer.innerHTML = '';
    resultContainer.appendChild(errorDiv);
}

// Add to history
function addToHistory(prompt, model, result, memoryUsage) {
    const historyItem = {
        prompt,
        model,
        result,
        timestamp: new Date().toLocaleString(),
        memoryUsage: memoryUsage || null
    };
    
    generationHistory.unshift(historyItem);
    
    // Keep only last 10 items
    if (generationHistory.length &amp;gt; 10) {
        generationHistory.pop();
    }
    
    updateHistoryDisplay();
}

// Update history display
function updateHistoryDisplay() {
    historyList.innerHTML = '';
    
    if (generationHistory.length === 0) {
        const emptyMessage = document.createElement('p');
        emptyMessage.style.color = '#9ca3af';
        emptyMessage.style.textAlign = 'center';
        emptyMessage.textContent = 'No generation history yet';
        historyList.appendChild(emptyMessage);
        return;
    }
    
    generationHistory.forEach((item, index) =&amp;gt; {
        const historyItemDiv = document.createElement('div');
        historyItemDiv.className = 'history-item';
        
        const promptDiv = document.createElement('div');
        promptDiv.className = 'history-item-prompt';
        promptDiv.textContent = item.prompt;
        historyItemDiv.appendChild(promptDiv);
        
        const modelDiv = document.createElement('div');
        modelDiv.className = 'history-item-model';
        modelDiv.textContent = item.model;
        historyItemDiv.appendChild(modelDiv);
        
        const timeDiv = document.createElement('div');
        timeDiv.className = 'history-item-time';
        timeDiv.textContent = item.timestamp;
        historyItemDiv.appendChild(timeDiv);
        
        if (item.memoryUsage) {
            const memoryDiv = document.createElement('div');
            memoryDiv.className = 'history-item-memory';
            memoryDiv.textContent = `Memory: RSS +${item.memoryUsage.rssDiff}MB, Heap +${item.memoryUsage.heapUsedDiff}MB`;
            historyItemDiv.appendChild(memoryDiv);
        }
        
        historyItemDiv.addEventListener('click', () =&amp;gt; {
            promptInput.value = item.prompt;
            modelSelect.value = item.model;
            displayResult({ result: item.result }, item.prompt, item.model);
        });
        
        historyList.appendChild(historyItemDiv);
    });
}

// Allow Enter key to submit (with Shift+Enter for new line)
// Ignore Enter key during Japanese IME composition
promptInput.addEventListener('keydown', (e) =&amp;gt; {
    if (e.key === 'Enter' &amp;amp;&amp;amp; !e.shiftKey &amp;amp;&amp;amp; !e.isComposing) {
        e.preventDefault();
        generateImage();
    }
});

// Made with Bob
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;server.js&lt;/strong&gt;: The &lt;code&gt;server.js&lt;/code&gt; file has been completely refactored into a &lt;strong&gt;modular, well-documented architecture&lt;/strong&gt; using helper functions. To improve observability, we integrated c&lt;strong&gt;omprehensive memory tracking&lt;/strong&gt; (monitoring RSS, heap, and external usage), alongside centralized configuration constants for easier maintenance. Finally, the service is now more resilient and easier to troubleshoot thanks to &lt;strong&gt;enhanced error handling&lt;/strong&gt;, robust response parsing, and the addition of conditional debug logging.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const express = require('express');
const cors = require('cors');
const axios = require('axios');
const { exec } = require('child_process');
const util = require('util');
const path = require('path');

const execPromise = util.promisify(exec);

const app = express();
const PORT = process.env.PORT || 3000;
const OLLAMA_API_URL = 'http://localhost:11434';
const MAX_CONTENT_SIZE_BYTES = 50 * 1024 * 1024; // 50MB
const IMAGE_GENERATION_TIMEOUT_MS = 180000; // 3 minutes timeout for image generation
const OLLAMA_REQUEST_CONFIG = {
    timeout: IMAGE_GENERATION_TIMEOUT_MS,
    maxContentLength: MAX_CONTENT_SIZE_BYTES,
    maxBodyLength: MAX_CONTENT_SIZE_BYTES
};

app.use(cors());
app.use(express.json());
app.use(express.static('public'));

// ============================================================================
// Helper Functions
// ============================================================================

/**
 * Validate request body for image generation
 */
function validateRequest(body) {
    const { prompt, model } = body;
    if (!prompt || !model) {
        return { valid: false, error: 'Prompt and model are required' };
    }
    return { valid: true, prompt, model };
}

/**
 * Measure current memory usage
 */
function measureMemory() {
    return process.memoryUsage();
}

/**
 * Calculate and log memory usage difference
 */
function logMemoryUsage(memBefore, memAfter) {
    const memDiff = {
        rss: ((memAfter.rss - memBefore.rss) / 1024 / 1024).toFixed(2),
        heapTotal: ((memAfter.heapTotal - memBefore.heapTotal) / 1024 / 1024).toFixed(2),
        heapUsed: ((memAfter.heapUsed - memBefore.heapUsed) / 1024 / 1024).toFixed(2),
        external: ((memAfter.external - memBefore.external) / 1024 / 1024).toFixed(2)
    };
    
    debugLog('=== Memory Usage Before Generation ===');
    debugLog(`RSS: ${(memBefore.rss / 1024 / 1024).toFixed(2)} MB`);
    debugLog(`Heap Total: ${(memBefore.heapTotal / 1024 / 1024).toFixed(2)} MB`);
    debugLog(`Heap Used: ${(memBefore.heapUsed / 1024 / 1024).toFixed(2)} MB`);
    debugLog(`External: ${(memBefore.external / 1024 / 1024).toFixed(2)} MB`);
    
    debugLog('=== Memory Usage After Generation ===');
    debugLog(`RSS: ${(memAfter.rss / 1024 / 1024).toFixed(2)} MB (Δ ${memDiff.rss} MB)`);
    debugLog(`Heap Total: ${(memAfter.heapTotal / 1024 / 1024).toFixed(2)} MB (Δ ${memDiff.heapTotal} MB)`);
    debugLog(`Heap Used: ${(memAfter.heapUsed / 1024 / 1024).toFixed(2)} MB (Δ ${memDiff.heapUsed} MB)`);
    debugLog(`External: ${(memAfter.external / 1024 / 1024).toFixed(2)} MB (Δ ${memDiff.external} MB)`);
    
    return memDiff;
}

/**
 * Track memory usage for a generation operation
 */
function trackMemoryUsage(beforeMem) {
    const afterMem = measureMemory();
    return logMemoryUsage(beforeMem, afterMem);
}

/**
 * Conditional debug logging
 */
function debugLog(...args) {
    if (process.env.NODE_ENV === 'development' || process.env.DEBUG === 'true') {
        console.log(...args);
    }
}

/**
 * Extract base64 image data from various response formats
 */
function extractImageData(data) {
    if (data.image) return `data:image/png;base64,${data.image}`;
    if (data.images?.[0]) return `data:image/png;base64,${data.images[0]}`;
    if (Buffer.isBuffer(data)) return `data:image/png;base64,${data.toString('base64')}`;
    return null;
}

/**
 * Parse NDJSON string and return last line as JSON
 */
function parseNDJSON(data) {
    const lines = data.trim().split('\n');
    return JSON.parse(lines[lines.length - 1]);
}

/**
 * Handle string response (NDJSON format)
 */
function parseStringResponse(data, log) {
    log('Response is a string, length:', data.length);
    
    try {
        const parsed = parseNDJSON(data);
        log('Parsed response keys:', Object.keys(parsed));
        
        const imageData = extractImageData(parsed);
        if (imageData) {
            log('✓ Found image in parsed response');
            return { imageData, ollamaResponse: '' };
        }
        
        if (parsed.response) {
            log('Found response field, length:', parsed.response.length);
            return { imageData: null, ollamaResponse: parsed.response };
        }
    } catch (e) {
        log('Failed to parse as JSON:', e.message);
        return { imageData: null, ollamaResponse: data };
    }
    
    return { imageData: null, ollamaResponse: '' };
}

/**
 * Handle object/Buffer response
 */
function parseObjectResponse(data, log) {
    const imageData = extractImageData(data);
    if (imageData) {
        log('✓ Found image in response');
        return { imageData, ollamaResponse: '' };
    }
    
    if (data.response) {
        log('Response length:', data.response.length);
        return { imageData: null, ollamaResponse: data.response };
    }
    
    return { imageData: null, ollamaResponse: '' };
}

/**
 * Parse Ollama API response and extract image/text data
 *
 * CRITICAL: Ollama returns newline-delimited JSON (NDJSON) for image generation
 * Format: Each line is a separate JSON object
 * Example:
 * {&quot;model&quot;:&quot;x/flux2-klein:4b&quot;,&quot;created_at&quot;:&quot;...&quot;,&quot;response&quot;:&quot;&quot;,&quot;done&quot;:false}
 * {&quot;model&quot;:&quot;x/flux2-klein:4b&quot;,&quot;created_at&quot;:&quot;...&quot;,&quot;response&quot;:&quot;&quot;,&quot;done&quot;:false}
 * {&quot;model&quot;:&quot;x/flux2-klein:4b&quot;,&quot;created_at&quot;:&quot;...&quot;,&quot;done&quot;:true,&quot;image&quot;:&quot;base64data...&quot;}
 *
 * The LAST line contains the complete image in the 'image' field (singular, not 'images')
 */
function parseOllamaResponse(data) {
    debugLog('Ollama API response received');
    debugLog('Response data type:', typeof data);
    
    if (typeof data === 'object' &amp;amp;&amp;amp; !Buffer.isBuffer(data)) {
        debugLog('Response data keys:', Object.keys(data));
    }
    
    if (typeof data === 'string') {
        return parseStringResponse(data, debugLog);
    }
    
    if (typeof data === 'object' || Buffer.isBuffer(data)) {
        return parseObjectResponse(data, debugLog);
    }
    
    debugLog('✗ No image or response data found');
    return { imageData: null, ollamaResponse: '' };
}

/**
 * Build API response object
 */
function buildResponse({ imageData, ollamaResponse, model, memDiff, responseData }) {
    return {
        success: true,
        result: imageData || ollamaResponse || 'No image generated',
        model,
        hasImage: !!imageData,
        memoryUsage: {
            rssDiff: memDiff.rss,
            heapUsedDiff: memDiff.heapUsed
        },
        debug: {
            responseType: typeof responseData,
            isBuffer: Buffer.isBuffer(responseData),
            responseLength: ollamaResponse.length,
            hasImages: !!(responseData.images)
        }
    };
}

/**
 * Generate image using Ollama API
 */
async function generateImageWithOllama(model, prompt) {
    return axios.post(
        `${OLLAMA_API_URL}/api/generate`,
        { model, prompt, stream: false },
        OLLAMA_REQUEST_CONFIG
    );
}

/**
 * Handle error responses
 */
function handleError(error, res) {
    console.error('Error generating image:', error.message);
    if (error.response?.data) {
        console.error('Error details:', error.response.data);
    }
    if (error.stack) {
        console.error('Stack trace:', error.stack);
    }
    res.status(500).json({
        error: 'Failed to generate image',
        details: error.message,
        response: error.response?.data || ''
    });
}

// ============================================================================
// API Endpoints
// ============================================================================

/**
 * Endpoint to generate image using Ollama HTTP API
 * This endpoint handles image generation requests from the frontend
 */
app.post('/api/generate', async (req, res) =&amp;gt; {
    const { valid, error, prompt, model } = validateRequest(req.body);
    if (!valid) {
        return res.status(400).json({ error });
    }

    try {
        const memBefore = measureMemory();
        debugLog(`Generating image with model: ${model}`);

        const response = await generateImageWithOllama(model, prompt);
        const { imageData, ollamaResponse } = parseOllamaResponse(response.data);
        const memDiff = trackMemoryUsage(memBefore);
        
        res.json(buildResponse({
            imageData,
            ollamaResponse,
            model,
            memDiff,
            responseData: response.data
        }));

    } catch (error) {
        handleError(error, res);
    }
});

// Endpoint to check available mod
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15586/image-generation-with-ollama-is-back-with-japanese-korean-and-chinese-languages-support</guid>
<pubDate>Thu, 23 Apr 2026 05:55:01 +0000</pubDate>
</item>
</channel>
</rss>