Skip to content

Commit

Permalink
Closing the loop
Browse files Browse the repository at this point in the history
  • Loading branch information
seanchatmangpt committed Feb 10, 2024
1 parent 09b3589 commit 0396ff4
Show file tree
Hide file tree
Showing 45 changed files with 2,246 additions and 339 deletions.
79 changes: 76 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,81 @@
[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/chatmangpt/rdddy)
# Reactive Domain-Driven Design (RDDDY) Framework

# rdddy
## Overview

Reactive DDD framework with DSPy
The Reactive Domain-Driven Design (RDDDY) Framework is a Python-based solution designed to facilitate the development of reactive, domain-driven systems. At its core, the framework leverages the Actor model to encapsulate state and behavior, allowing for asynchronous message passing and ensuring loose coupling and enhanced system resilience. This README provides insights into the **why**, **what**, and **how** of utilizing the `ActorSystem` and `Actor` within the RDDDY framework.

## Why RDDDY?

In the landscape of modern software development, managing complexity and ensuring system reliability in the face of concurrent operations and distributed system architectures can be challenging. The RDDDY framework addresses these challenges by:

- **Encapsulating State and Behavior**: Each actor in the system manages its own state and behavior, reducing system complexity and enhancing modularity.
- **Facilitating Asynchronous Communication**: Actors communicate through asynchronous message passing, improving system responsiveness and scalability.
- **Promoting Domain-Driven Design**: The framework encourages the integration of domain-specific logic into the system's design, ensuring that software solutions are closely aligned with business requirements.
- **Enhancing Fault Tolerance**: By isolating actors and defining clear interaction patterns, the system improves its ability to recover from failures.

## What is the ActorSystem and Actor?

### ActorSystem

The `ActorSystem` acts as the orchestrator for actor lifecycle management, message passing, and system-wide coordination. It provides functionalities for:

- **Creating and Managing Actors**: Facilitates the instantiation and supervision of actors within the system.
- **Message Dispatching**: Supports both direct and broadcast messaging patterns, enabling actors to communicate asynchronously.
- **Maintaining System Invariants**: Ensures that the system's operational semantics and domain-specific assertions are preserved.

### Actor

`Actor` represents the fundamental unit of computation within the framework. Each actor:

- **Encapsulates its State**: Manages its internal state independently of other actors.
- **Processes Messages Asynchronously**: Handles incoming messages based on defined behaviors, allowing for reactive responses to events.
- **Interacts Through Message Passing**: Communicates with other actors via asynchronous message exchanges, without direct method calls.

## How to Use

### Implementing an Actor

Actors are implemented by subclassing the `Actor` class and defining message handlers for specific message types:

```python
class YourActor(Actor):
async def handle_your_message(self, event: YourEventType):
# Process the message.
pass
```

### Creating Actors in the ActorSystem

```python
actor_system = ActorSystem()
your_actor = await actor_system.actor_of(YourActor)
```

### Sending and Publishing Messages

```python
# Direct message to a specific actor
await actor_system.send(your_actor.actor_id, YourMessageType(...))

# Broadcast message to all actors
await actor_system.publish(YourMessageType(...))
```

### Error Handling and System Invariants

The framework supports robust error handling and the maintenance of system invariants. Actors can catch exceptions during message processing and the `ActorSystem` can broadcast error events, ensuring the system remains responsive and resilient:

```python
class FaultyActor(Actor):
async def receive(self, message: Message):
# Implementation that might raise an exception
```

The `ActorSystem` detects exceptions, broadcasting an `Event` with error details, which can be tested and verified through the framework's testing capabilities.

## Conclusion

The RDDDY framework offers a powerful paradigm for building scalable, resilient, and domain-driven systems in Python. By leveraging the Actor model and embracing asynchronous communication, developers can tackle the complexities of modern software development, ensuring their applications are both robust and closely aligned with business objectives.

## Using

Expand Down
11 changes: 11 additions & 0 deletions experiments/actor/assertion.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
2024-02-09 16:20:32,273 - dspy.primitives.assertions - ERROR - AssertionError: You need to create a kwargs dict for HygenTemplateSpecificationCommand

Validation error:
1 validation error for HygenTemplateSpecificationCommand
template_content
String should have at least 200 characters [type=string_too_short, input_value='This is the template for...he dashboard generator.', input_type=str]
For further information visit https://errors.pydantic.dev/2.5/v/string_too_short
2024-02-09 16:20:34,397 - dspy.primitives.assertions - ERROR - AssertionError: You need to create a kwargs dict for HygenTemplateSpecificationCommand

Validation error:
invalid syntax. Perhaps you forgot a comma? (<unknown>, line 1)
1 change: 1 addition & 0 deletions experiments/actor/dashboard generator/about.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
new action for about route
3 changes: 2 additions & 1 deletion experiments/actor/hoare2.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rdddy.actor import Actor
from rdddy.actor_system import ActorSystem
from experiments.actor.messages import *
from experiments.actor.messages import StartPhaseCommand, EvaluatePreconditionQuery, PreconditionEvaluatedEvent, \
ProcessPhaseCommand, PhaseErrorEvent, EvaluatePostconditionQuery, PostconditionEvaluatedEvent, PhaseCompletedEvent


class InitiationActor(Actor):
Expand Down
58 changes: 58 additions & 0 deletions experiments/actor/hygen_cot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import dspy

lm = dspy.OpenAI(max_tokens=2000)
dspy.settings.configure(lm=lm)


class GenerateHygenTemplate(dspy.Signature):
"""Generate a Hygen template based on specified requirements.
---
to: app/emails/<%= name %>.html
---
Hello <%= name %>,
<%= message %>
(version <%= version %>)
"""

requirements = dspy.InputField(desc="Specifications or requirements for the Hygen template")
template = dspy.OutputField(desc="Generated Hygen template code")


class HygenTemplateGenerator(dspy.Module):
def __init__(self):
super().__init__()
self.generate_template = dspy.ChainOfThought(GenerateHygenTemplate)

def forward(self, requirements):
# The ChainOfThought could involve parsing the requirements,
# determining the structure and variables needed for the Hygen template,
# and then constructing the template code.
template_code = self.generate_template(requirements=requirements).template
return dspy.Prediction(template=template_code)


def main():
# Example usage
generator = HygenTemplateGenerator()

# Define your requirements here. This should be a detailed description of what the Hygen template needs to do.

requirements = """Generate a React component with props, state, and a basic render function. The component should be a functional
component using React Hooks. """

# Generate the Hygen template
pred = generator(requirements)

# Output the generated template
print(f"Generated Hygen Template:\n{pred.template}")

# Write pred.template to disk

with open("template.ejs.t", "w") as f:
f.write(pred.template)


if __name__ == '__main__':
main()
117 changes: 117 additions & 0 deletions experiments/actor/hygen_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import dspy
import os

from pydantic import Field
from loguru import logger

from rdddy.actor import Actor
from rdddy.actor_system import ActorSystem
from rdddy.generators.gen_pydantic_instance import GenPydanticInstance
from rdddy.messages import *

template_content_desc = """
---
to: app/emails/<%= name %>.html
---
Hello <%= name %>,
<%= message %>
(version <%= version %>)
"""

class HygenTemplateSpecificationCommand(Command):
template_name: str = Field(..., description="The name of the template to generate.")
template_path: str = Field(..., description="The path where the template should be created.")
template_content: str = Field(..., description=template_content_desc, min_length=200)


class TemplateGeneratedEvent(Event):
template_name: str = Field(..., description="The name of the generated template.")
success: bool = Field(..., description="Indicates whether the template was successfully generated.")


class TemplateValidatedEvent(Event):
template_name: str = Field(..., description="The name of the validated template.")
is_valid: bool = Field(..., description="Indicates the validation result of the template.")


class TemplateDeploymentStartedEvent(Event):
template_name: str = Field(..., description="The name of the template being deployed.")


class TemplateDeploymentCompletedEvent(Event):
template_name: str = Field(..., description="The name of the deployed template.")
success: bool = Field(..., description="Indicates whether the template was successfully deployed.")


class HygenTemplateGeneratorActor(Actor):
async def handle_hygen_template_specification_command(self, command: HygenTemplateSpecificationCommand):
# Generate the Hygen template
try:
self.write_to_file(f"{command.template_path}/{command.template_name}.ejs.t", command.template_content)
logger.debug(f"Hygen template {command.template_name} generated successfully.")
await self.publish(TemplateGeneratedEvent(template_name=command.template_name, success=True))
except Exception as e:
logger.debug(f"Failed to generate template {command.template_name}: {e}")
await self.publish(TemplateGeneratedEvent(template_name=command.template_name, success=False))

def write_to_file(self, file_path: str, content: str):
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "w") as file:
file.write(content)


class TemplateValidationActor(Actor):
async def handle_template_generated_event(self, event: TemplateGeneratedEvent):
if event.success:
# Simulate validation logic
logger.debug(f"Validating template: {event.template_name}")
# Assume validation passes for this example
await self.publish(TemplateValidatedEvent(template_name=event.template_name, is_valid=True))


class TemplateDeploymentActor(Actor):
async def handle_template_validated_event(self, event: TemplateValidatedEvent):
if event.is_valid:
logger.debug(f"Deploying template: {event.template_name}")
# Simulate deployment logic
# Assume deployment succeeds for this example
await self.publish(TemplateDeploymentCompletedEvent(template_name=event.template_name, success=True))


async def setup_and_run():
actor_system = ActorSystem()
# Initialize and register all actors
await actor_system.actors_of([
HygenTemplateGeneratorActor,
TemplateValidationActor,
TemplateDeploymentActor
])

module = GenPydanticInstance(root_model=HygenTemplateSpecificationCommand)
hygen_inst = module.forward(
"I need a hygen template to create an about page, use the dashboard generator. and the 'new' action. the route is about")

await actor_system.publish(hygen_inst)
# Trigger the template generation process
# await actor_system.publish(HygenTemplateSpecificationCommand(
# template_name="exampleTemplate",
# template_path="_templates/example/new",
# template_content="---\nto: app/example/<%= name %>.js\n---\nconsole.log('Hello, <%= name %>!')"
# ))


import asyncio


async def main():
lm = dspy.OpenAI(max_tokens=2000)
dspy.settings.configure(lm=lm)

await setup_and_run()


# if __name__ == '__main__':
# asyncio.run(main())


17 changes: 17 additions & 0 deletions experiments/actor/template.ejs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
to: src/components/<%= name %>.js
---

import React, { useState } from 'react';

const <%= name %> = (props) => {
const [state, setState] = useState();

return (
<div>
{/* Component code here */}
</div>
);
};

export default <%= name %>;
3 changes: 1 addition & 2 deletions experiments/chatgpt_conversation_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from pydantic import BaseModel, Field, ValidationError
from typing import List, Optional, Any
from decimal import Decimal

class Author(BaseModel):
role: str
Expand Down Expand Up @@ -53,7 +52,7 @@ def process_conversations_chunk(chunk):
data = Data(**conversation.mapping[key])
if data.message and data.message.author.role == "assistant":
for part in data.message.content.parts:
if "CoRL" in part:
if "calculus" in part:
print(part)
# encoding = tiktoken.encoding_for_model("text-embedding-ada-002")
# print(len(encoding.encode(part)))
Expand Down
Loading

0 comments on commit 0396ff4

Please sign in to comment.