Skip to content

Commit

Permalink
Class creation
Browse files Browse the repository at this point in the history
  • Loading branch information
seanchatmangpt committed Feb 2, 2024
1 parent b2e3801 commit 88b496d
Show file tree
Hide file tree
Showing 18 changed files with 410 additions and 49 deletions.
68 changes: 52 additions & 16 deletions experiments/chatgpt_conversation_parser.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
import ijson
import tiktoken

# Define the path to your large JSON file
json_file_path = "/Users/candacechatman/Downloads/1dd9be16ec32302ab8cb0994090949c55e08baa435443c4b5e8f7f8279720e41-2024-01-12-05-55-50/conversations.json"
json_file_path = "/Users/candacechatman/dev/rdddy/data/conversations.json"


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

class Author(BaseModel):
role: str
name: Optional[str] = None
metadata: dict

class ContentPart(BaseModel):
content_type: str
parts: List[str] | None

class Message(BaseModel):
id: str
author: Author
content: ContentPart
status: str
metadata: dict

class Data(BaseModel):
id: str
message: Message | None
parent: str | None
children: List[str]


class Conversation(BaseModel):
title: str
mapping: dict


# Function to process each conversation chunk
Expand All @@ -10,22 +43,25 @@ def process_conversations_chunk(chunk):
from pydantic import BaseModel
from typing import List

class Message(BaseModel):
author: str
text: str

class Conversation(BaseModel):
title: str
messages: List[Message]

# Process each conversation in the chunk
for data in chunk:
conversation = Conversation(**data)
# Do whatever processing you need with the conversation
print(f"Title: {conversation.title}")
for message in conversation.messages:
print(f"Author: {message.author}")
print(f"Text: {message.text}")
for chunked in chunk:
try:
conversation = Conversation(**chunked)
# Do whatever processing you need with the conversation
# print(f"Title: {conversation.title}")
for key in conversation.mapping:
data = Data(**conversation.mapping[key])
if data.message and data.message.author.role == "assistant":
for part in data.message.content.parts:
if "{{" in part:
print(part)
# encoding = tiktoken.encoding_for_model("text-embedding-ada-002")
# print(len(encoding.encode(part)))
# print(part)
except ValidationError as e:
# print(e)
pass



# Open the JSON file for streaming
Expand Down
8 changes: 4 additions & 4 deletions experiments/collaborative_agent.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from slss.actor_system import ActorSystem
from slss.agent import Agent
from domain.collaboration_context import *
from rdddy.actor import Actor
from rdddy.actor_system import ActorSystem
from slss.domain.collaboration_context import *


class CollaborativeAgent(Agent):
class CollaborativeAgent(Actor):
def handle_agent_created(self, event: AgentCreated):
print(f"Agent {event.agent_id} created with name: {event.agent_name}")

Expand Down
43 changes: 43 additions & 0 deletions experiments/output_signature.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import dspy

class GenJinjaSignature(dspy.Signature):
"""
This signature transforms source code into Jinja templates, adhering to best practices for template creation.
It is designed to take a source code snippet, analyze its structure, and produce a corresponding Jinja template.
The output template will be well-structured, maintain readability, and include verbose instructions for clarity.
Best practices incorporated include clear variable naming, usage of control structures for dynamic content,
ample documentation within the template, and maintaining a structure that mirrors the source while allowing for
flexibility and scalability in template rendering.
"""
source = dspy.InputField(
prefix="Convert to a Jinja Template: ",
desc="The source code snippet to be converted into a Jinja template. The source should be a valid Python code structure, such as a class or a function, that you wish to render dynamically using Jinja."
)
jinja_template = dspy.OutputField(
prefix="```jinja\n",
desc="The Jinja template generated from the provided source. This template will embody best practices in template creation, ensuring clarity, maintainability, and ease of use. The template will include dynamic placeholders, control statements, and documentation comments as necessary."
)


SOURCE = '''class GenerateSearchQuery(dspy.Signature):
"""Write a simple search query that will help answer a complex question."""
context = dspy.InputField(desc="may contain relevant facts")
question = dspy.InputField()
query = dspy.OutputField()
### inside your program's __init__ function
self.generate_answer = dspy.ChainOfThought(GenerateSearchQuery)'''


def main():
lm = dspy.OpenAI(max_tokens=500)
dspy.settings.configure(lm=lm)
cot = dspy.ChainOfThought(GenJinjaSignature)
template = cot.forward(source=SOURCE).jinja_template
print(template)


if __name__ == '__main__':
main()
2 changes: 1 addition & 1 deletion experiments/workshop_aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def __init__(self, actor_system, workshop_id):
self.events = []

async def start_workshop(self, command: StartWorkshop):
# Start workshop logic
# Start workshop logic
# Emit WorkshopStarted event
print("Starting workshop")
await self.emit_event(WorkshopStarted(workshop_id=command.workshop_id))
Expand Down
4 changes: 4 additions & 0 deletions src/rdddy/generators/assertion.log
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ You will be penalized for not returning only a set for set_python_primitive_stri
2024-01-24 10:46:13,339 - dspy.primitives.assertions - ERROR - AssertionError: You need to create a valid python set primitive type for
set_python_primitive_string
You will be penalized for not returning only a set for set_python_primitive_string
2024-02-01 18:35:23,495 - dspy.primitives.assertions - ERROR - AssertionError: You need to create a kwargs dict for PydanticClassTemplateSpecificationModel
2024-02-01 18:35:23,496 - dspy.primitives.assertions - ERROR - AssertionError: You need to create a kwargs dict for PydanticClassTemplateSpecificationModel
2024-02-01 18:36:14,720 - dspy.primitives.assertions - ERROR - AssertionError: You need to create a kwargs dict for PydanticClassTemplateSpecificationModel
2024-02-01 18:36:14,721 - dspy.primitives.assertions - ERROR - AssertionError: You need to create a kwargs dict for PydanticClassTemplateSpecificationModel
17 changes: 17 additions & 0 deletions src/rdddy/generators/contact_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from datetime import datetime

from pydantic import BaseModel, Field, validator, root_validator, EmailStr, UrlStr

class ContactModel(BaseModel):
"""A Pydantic model representing a contact in the friend of a friend ontology."""
name: str = Field(default=None, title="", description="The name of the contact.", min_length=2, max_length=50)
email: EmailStr = Field(default=None, title="", description="The email address of the contact.", min_length=5, max_length=50)
phone_number: str = Field(default=None, title="", description="The phone number of the contact.", min_length=10, max_length=15)
address: str = Field(default=None, title="", description="The address of the contact.", min_length=10, max_length=100)
birthday: datetime = Field(default=None, title="", description="The birthday of the contact.", ge=1900, le=2021)
relationship: str = Field(default=None, title="", description="The relationship of the contact to the user.", min_length=2, max_length=50)
notes: str = Field(default=None, title="", description="Any additional notes or information about the contact.", max_length=500)
social_media: str = Field(default=None, title="", description="The social media accounts of the contact.", max_length=100)
company: str = Field(default=None, title="", description="The company the contact works for.", min_length=2, max_length=50)
job_title: str = Field(default=None, title="", description="The job title of the contact.", min_length=2, max_length=50)

6 changes: 6 additions & 0 deletions src/rdddy/generators/example_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from pydantic import BaseModel, Field, validator, root_validator, EmailStr, UrlStr

class ExampleModel(BaseModel):
"""An example Pydantic model for demonstration."""
email: EmailStr = Field(default='[email protected]', title="", description="User's email address")

161 changes: 161 additions & 0 deletions src/rdddy/generators/gen_pydantic_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import dspy
import inflection
import jinja2
from pydantic import BaseModel, Field

from rdddy.generators.gen_pydantic_instance import GenPydanticInstance
from typetemp.functional import render
from typing import List, Optional


class FieldTemplateSpecificationModel(BaseModel):
field_name: str = Field(
...,
title="Field Name",
description="The name of the field in the model.",
)
field_type: str = Field(
...,
title="Field Type",
description="The data type of the field, e.g., 'str', 'int', 'EmailStr', or 'datetime'.",
)
default_value: str | None = Field(
"...",
title="Default Value",
description="The default value for the field if not provided.",
)
description: str = Field(
...,
title="Description",
description="A detailed description of the field's purpose and usage.",
)
constraints: str | None = Field(
None,
title="Constraints",
description="Constraints or validation rules for the field, if any. Specify as a string, e.g., 'min_length=2, max_length=50' or 'ge=0, le=120'.",
)


class ConfigTemplateSpecificationModel(BaseModel):
class Config:
title = "Model Configuration"
description = "Configuration settings for a Pydantic BaseModel."

title: str = Field(
...,
description="The title for the BaseModel configuration.",
)
description: str = Field(
...,
description="A detailed description of the BaseModel configuration's purpose and usage.",
)
allow_population_by_field_name: bool = Field(
True,
description="Whether to allow populating a model using field names.",
)
underscore_attrs_are_private: bool = Field(
False,
description="Whether to treat underscore-prefixed attributes as private (no validation).",
)
alias_generator: str = Field(
...,
description="The alias generator to use for field aliasing.",
)


class ValidatorTemplateSpecificationModel(BaseModel):
validator_name: str = Field(
...,
title="Validator Name",
description="The name of the validator.",
)
description: str = Field(
...,
title="Description",
description="A detailed description of the validator's purpose and usage.",
)
parameters: List[str] = Field(
[],
title="Parameters",
description="A list of parameter names accepted by the validator.",
)


class PydanticClassTemplateSpecificationModel(BaseModel):
class_name: str = Field(
...,
title="Model Class Name",
description="The class name of the Pydantic model.",
)
description: str = Field(
...,
title="Description",
description="A detailed description of the Pydantic model's purpose and usage.",
)
fields: List[FieldTemplateSpecificationModel] = Field(
...,
title="Fields",
description="A list of field specifications for the model. Each field specifies the name, type, default value, description, and constraints.",
)



template_str = '''from pydantic import BaseModel, Field, validator, root_validator, EmailStr, UrlStr
from typing import List, Optional
from datetime import datetime
class {{ model.class_name }}(BaseModel):
"""{{ model.description }}"""
{% for field in model.fields %}
{{ field.field_name }}: {{ field.field_type }} = Field(default={{ field.default_value }}, title="{{ field.title }}", description="{{ field.description }}"{% if field.constraints %}, {{ field.constraints }}{% endif %})
{% endfor %}
{% if model.validators|length > 0 %}
{% for validator in model.validators %}
@validator('{{ validator.parameters|join("', '") }}')
def {{ validator.validator_name }}(cls, value):
# {{ validator.description }}
return value
{% endfor %}
{% endif %}
{% if model.config %}
class Config:
{% if model.config.allow_population_by_field_name %}allow_population_by_field_name = True{% endif %}
{% if model.config.underscore_attrs_are_private %}underscore_attrs_are_private = True{% endif %}
{% if model.config.alias_generator %}alias_generator = {{ model.config.alias_generator }}{% endif %}
{% endif %}
'''

def render_pydantic_class(model_spec, template_str):
template = jinja2.Template(template_str)
return template.render(model=model_spec)


def write_pydantic_class_to_file(class_str, filename):
with open(filename, 'w') as file:
file.write(class_str)


# Example usage
def main():
lm = dspy.OpenAI(max_tokens=1000)
dspy.settings.configure(lm=lm)

model_prompt = "I need a verbose contact model named ContactModel from the friend of a friend ontology with 10 fields, each with length constraints"

model_module = GenPydanticInstance(root_model=PydanticClassTemplateSpecificationModel,
child_models=[FieldTemplateSpecificationModel
])

model_inst = model_module.forward(model_prompt)

# Render the Pydantic class from the specification
rendered_class_str = render(template_str, model=model_inst)

# Write the rendered class to a Python file
write_pydantic_class_to_file(rendered_class_str, f"{inflection.underscore(model_inst.class_name)}.py")


if __name__ == '__main__':
main()
Loading

0 comments on commit 88b496d

Please sign in to comment.