diff --git a/.envDemo b/.envDemo new file mode 100644 index 0000000..a7748b0 --- /dev/null +++ b/.envDemo @@ -0,0 +1 @@ +GROQ_API_KEY="gsk_xxx_put_in_your_own_key_xxx" \ No newline at end of file diff --git a/README.md b/README.md index 4f88f5b..e78b54f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,36 @@ -[![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/user/my-package) +# DSPyGen: Phyton - for JavaScript use: npm dpgjs +The idea of this fork is to follow up with the genius developer Sean Chatman and his work (https://github.com/seanchatmangpt/dspygen) and find out how to best and quickly get started producing the best quality. + +The purpose is to use it mostly everywhere: Expert chatbots, workflows, code/data retrievers... + +Thanks, and find out about the great DSPy project here https://github.com/stanfordnlp/dspy. + +# My ideas: Structuring Code - A new class of Digital Assets = Dematerialized Commodity +Coming from Financial Engineering and Structuring Financial Products and also from Software Engineering to Avaloq Param, my goals are about getting all Code-Gen Systems to work in compliance and not just mixing others' IP as AI seems mostly to do today. + +For such, any creator of something new and valuable should have options to receive a (micro-) payment any time their code is analyzed, cloned, or even used, especially at the enterprise level, where masses of payments might just not be paid. + +The bases for all valuable, useful code should then be a new form of NFT - a Structured Commodity of Code - a variant of a Dematerialized Asset - such as I earlier created my Meta-Bricks Repo for. + +Having a massive store of runnable and easily pluggable/composable elements of code, paired with terms and conditions we know from classical structured products (see e.g., Ricardian Contracts), Retrievers should use those for Code-Gen workflows, especially to keep legal risks lowest and always send payments or pay shares/revenues from their new meta-bricks derived to the creators owning the underlying product. But we are not here yet, and not many LLMs can and will reference where the code was taken from... I'm sure that can be fixed now. + +# Getting Started - Find out simple structures of game code or how creators can get into 'owning' and incentivize code +First: Also in terms of privacy / data loss protection, I try to switch the init to use ollama since this week llama3 came out with decent figures. + +To install ollama first go: https://ollama.com/ + +The default LLM should be set to model="llama3:8b-instruct-q5_1" or "llama3:70b-instruct-q3_K_M" +use +from dspygen.lm.groq_lm import Groq +from dspygen.lm.ollama_lm import Ollama +and the init: +init_dspy(Ollama, model="llama3:8b-instruct-q5_1", max_tokens=8000) +or just run the blog_mudule.py + +Use Groq - get the API KEY from https://console.groq.com/keys +and modify your .env like seen in .envDemo +dont forget to init +init_dspy(Groq, model="llama3-70b-8192", max_tokens=8000) # DSPyGen: Streamlining AI Development @@ -17,7 +49,48 @@ DSPyGen, influenced by the efficiency and modularity of Ruby on Rails, is a powe ## Getting Started -Ensure Python is installed on your system. Install DSPyGen via pip: + +[![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/user/my-package) + +or + +Ensure Python is installed on your system. + +Devs and local changes seems to be always compiled ok with conda for env management + +conda create -n dspygen_py python=3.10 +conda activate dspygen_py +poetry config virtualenvs.create false + +in case needed +poetry update or add + +using VS Code - Confirm correct (conda env!) Python Interpreter in VS Code +Ensure that VS Code is using the correct Python interpreter from your virtual environment where dspy-ai is installed: + +- Open Command Palette in VS Code: Use Ctrl+Shift+P or Cmd+Shift+P on macOS. +- Select Interpreter: Type and select "Python: Select Interpreter." +- Choose the Correct Environment: Pick the interpreter from the virtual environment associated with your project (\envs\dspygen_py). + +pip install -e . develop + +on Win get your CLI up to speed and alias + +Set-Alias -Name dg -Value dspygen + +and + +dg --help + +should work + +try and run the blog-creator and run +src\dspygen\modules\blog_module.py +compare to my run: +src\dspygen\experiments\blog\Tetris_1.md + +For production envs: ```bash pip install dspygen diff --git a/src/dspygen/cli.py b/src/dspygen/cli.py index ae35b3f..359dce3 100644 --- a/src/dspygen/cli.py +++ b/src/dspygen/cli.py @@ -3,13 +3,10 @@ import sys from importlib import import_module, metadata import subprocess - - import os from pathlib import Path -import inflection import typer from munch import Munch diff --git a/src/dspygen/experiments/blog/Tetris_1.md b/src/dspygen/experiments/blog/Tetris_1.md new file mode 100644 index 0000000..7748960 --- /dev/null +++ b/src/dspygen/experiments/blog/Tetris_1.md @@ -0,0 +1,67 @@ +# The Tetris Game, Simple but Working +*Running the blog_module.py with Subject "The Tetris Game, simple but working : in 300 lines" on Ollama : llama3:8b-instruct-q5_1 max_tokens=8000 + +The classic Tetris game is a timeless favorite among gamers. In this article, we will explore how to create a basic version of the game using Python. We'll focus on simplicity and functionality over graphics and advanced features. + +## Setting Up the Game Board + +First, let's set up our game board as a 2D list with dimensions `10x20`. This will represent the grid where our Tetris pieces will fall. +```python +board = [[' ' for _ in range(20)] for _ in range(10)] +``` +## Defining the Tetrominoes + +Next, we'll define the different shapes that make up our Tetris pieces. We'll use a list of lists to represent each shape: +```python +tetrominoes = [ + [[1, 1], [1, 0]], # I-Shape + [[1, 1, 1], [0, 1, 0]], # O-Shape + [[1, 0, 0], [1, 1, 0]], # T-Shape + [[1, 1, 0], [0, 1, 1]], # L-Shape + [[0, 1, 1], [1, 1, 0]] # J-Shape +] +``` +## Generating the Game Loop + +Now we'll create a game loop that will handle user input and update our game board accordingly: +```python +while True: + # Get user input (arrow keys) + direction = input("Enter direction (up/down/left/right): ") + + # Update the Tetromino's position based on user input + if direction == 'up': + tetromino.y -= 1 + elif direction == 'down': + tetromino.y += 1 + elif direction == 'left': + tetromino.x -= 1 + elif direction == 'right': + tetromino.x += 1 + + # Check for collisions with the game board and other Tetrominos + if not is_valid_position(tetromino): + print("Game Over!") + break + + # Update the game board with the new Tetromino position + update_board(board, tetromino) +``` +## Handling Collisions and Game Over + +We'll also need to handle collisions between our Tetromino and the game board, as well as other Tetrominos. This will ensure that our game doesn't crash or become stuck: +```python +def is_valid_position(tetromino): + for cell in tetromino.cells: + if (cell.x < 0 or cell.x >= len(board[0]) or + cell.y < 0 or cell.y >= len(board)): + return False + return True + +def update_board(board, tetromino): + for cell in tetromino.cells: + board[cell.y][cell.x] = '#' +``` +## Conclusion + +And that's it! With these simple steps, we've created a basic Tetris game using Python. Of course, there are many ways to improve and expand upon this code, but this should give you a good starting point for your own projects. \ No newline at end of file diff --git a/src/dspygen/experiments/blog/Tetris_LMStud_Llama3.py b/src/dspygen/experiments/blog/Tetris_LMStud_Llama3.py new file mode 100644 index 0000000..29ebd20 --- /dev/null +++ b/src/dspygen/experiments/blog/Tetris_LMStud_Llama3.py @@ -0,0 +1,98 @@ +import pygame +import random + +# Initialize PyGame +pygame.init() + +# Define constants +WIDTH, HEIGHT = 400, 500 +BLOCK_SIZE = 20 +FPS = 60 + +# Define colors +WHITE = (255, 255, 255) +RED = (255, 0, 0) +BLUE = (0, 0, 255) + +class Piece: + def __init__(self): + self.x = 0 + self.y = 0 + self.matrix = [[1, 1], [1, 1]] + +class Game: + def __init__(self): + self.board = [[0 for _ in range(10)] for _ in range(20)] + self.pieces = [] + self.score = 0 + self.clock = pygame.time.Clock() + + def check_collision(self, piece): + for y, row in enumerate(piece.matrix): + for x, val in enumerate(row): + if val == 1: + if (x + piece.x >= 10) or (y + piece.y >= 20) or \ + self.board[y + piece.y][x + piece.x] != 0: + return True + return False + + def update_screen(self, screen): + for y, row in enumerate(self.board): + for x, val in enumerate(row): + if val != 0: + pygame.draw.rect(screen, BLUE, (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)) + + def game_loop(self, screen): + while True: + self.clock.tick(FPS) + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + + piece = Piece() + while True: + piece.x = random.randint(0, 9) + piece.y = 0 + if not self.check_collision(piece): + break + + while True: + self.update_screen(screen) + pygame.display.flip() + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + + keys = pygame.key.get_pressed() + if keys[pygame.K_LEFT]: + piece.x -= 1 + if self.check_collision(piece): + piece.x += 1 + if keys[pygame.K_RIGHT]: + piece.x += 1 + if self.check_collision(piece): + piece.x -= 1 + + if random.randint(0, 100) < 10: # Randomly move the piece down + piece.y += 1 + if self.check_collision(piece): + for y, row in enumerate(piece.matrix): + for x, val in enumerate(row): + print(val) + if val == 1: + print(x,y) + #self.board[y + piece.y][x + piece.x] = 1 + break + + for y, row in enumerate(self.board): + if all(val == 1 for val in row): + del self.board[y] + self.score += 10 + +if __name__ == "__main__": + screen = pygame.display.set_mode((WIDTH, HEIGHT)) + pygame.display.set_caption("Tetris") + game = Game() + game.game_loop(screen) \ No newline at end of file diff --git a/src/dspygen/experiments/blog/Tetris_groq.py b/src/dspygen/experiments/blog/Tetris_groq.py new file mode 100644 index 0000000..56940de --- /dev/null +++ b/src/dspygen/experiments/blog/Tetris_groq.py @@ -0,0 +1,79 @@ +import pygame +import random + +pygame.init() + +WIDTH, HEIGHT = 800, 600 +WHITE = (255, 255, 255) +BLACK = (0, 0, 0) +RED = (255, 0, 0) + +class Tetris: + def __init__(self): + self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) + pygame.display.set_caption('Tetris') + self.clock = pygame.time.Clock() + self.grid = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)] + self.next_piece = self.get_random_piece() + self.current_piece = self.next_piece + self.next_piece = self.get_random_piece() + self.score = 0 + self.x = 0 + self.y = 0 + + def get_random_piece(self): + pieces = ['I', 'J', 'L', 'O', 'S', 'T', 'Z'] + return self.get_piece(pieces) + + def get_piece(self, pieces): + return random.choice(pieces) + + def draw_grid(self): + for i in range(HEIGHT): + for j in range(WIDTH): + if self.grid[i][j] == 1: + pygame.draw.rect(self.screen, BLACK, (j, i, 1, 1)) + + def draw_current_piece(self): + for i in range(len(self.current_piece)): + for j in range(len(self.current_piece[i])): + if self.current_piece[i][j] == 1: + pygame.draw.rect(self.screen, RED, ((j + self.x, i + self.y), 1, 1)) + + def move_piece(self): + self.x += 1 + if self.x + len(self.current_piece[0]) > WIDTH: + self.x = 0 + self.y += 1 + if self.y + len(self.current_piece) > HEIGHT: + self.y = 0 + self.current_piece = self.next_piece + self.next_piece = self.get_random_piece() + self.score += 1 + + def run(self): + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_LEFT: + self.x -= 1 + elif event.key == pygame.K_RIGHT: + self.x += 1 + elif event.key == pygame.K_DOWN: + self.y += 1 + elif event.key == pygame.K_UP: + self.y -= 1 + self.move_piece() + self.screen.fill(WHITE) + self.draw_grid() + self.draw_current_piece() + pygame.display.flip() + self.clock.tick(60) + pygame.quit() + +if __name__ == '__main__': + game = Tetris() + game.run() \ No newline at end of file diff --git a/src/dspygen/experiments/blog/Tetris_groq_llama3_80.py b/src/dspygen/experiments/blog/Tetris_groq_llama3_80.py new file mode 100644 index 0000000..27b68e5 --- /dev/null +++ b/src/dspygen/experiments/blog/Tetris_groq_llama3_80.py @@ -0,0 +1,99 @@ +import pygame +import random + +pygame.init() + +WIDTH, HEIGHT = 800, 600 +BLACK = (0, 0, 0) +WHITE = (255, 255, 255) +RED = (255, 0, 0) +GREEN = (0, 255, 0) +BLUE = (0, 0, 255) +YELLOW = (255, 255, 0) +MAGENTA = (255, 0, 255) +CYAN = (0, 255, 255) +ORANGE = (255, 165, 0) + +class Tetris: + def __init__(self): + self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) + pygame.display.set_caption('Tetris') + self.clock = pygame.time.Clock() + self.grid = [[0 for _ in range(10)] for _ in range(20)] + self.next_piece = self.get_random_piece() + self.current_piece = self.next_piece + self.next_piece = self.get_random_piece() + self.score = 0 + self.x = 0 + self.y = 0 + self.speed = 1 + + def get_random_piece(self): + pieces = ['I', 'J', 'L', 'O', 'S', 'T', 'Z'] + return self.get_piece(pieces) + + def get_piece(self, pieces): + return random.choice(pieces) + + def draw_grid(self): + for i in range(20): + for j in range(10): + if self.grid[i][j] == 1: + pygame.draw.rect(self.screen, BLACK, (j * 30, i * 30, 30, 30), 1) + + def draw_current_piece(self): + for i in range(len(self.current_piece)): + for j in range(len(self.current_piece[i])): + if self.current_piece[i][j] == 1: + if self.current_piece == 'I': + color = CYAN + elif self.current_piece == 'J': + color = BLUE + elif self.current_piece == 'L': + color = ORANGE + elif self.current_piece == 'O': + color = YELLOW + elif self.current_piece == 'S': + color = GREEN + elif self.current_piece == 'T': + color = MAGENTA + elif self.current_piece == 'Z': + color = RED + pygame.draw.rect(self.screen, color, ((j + self.x) * 30, (i + self.y) * 30, 30, 30)) + + def move_piece(self): + self.y += 1 + if self.y + len(self.current_piece) > 20: + self.y = 0 + self.current_piece = self.next_piece + self.next_piece = self.get_random_piece() + self.score += 1 + self.speed += 0.1 + + def run(self): + running = True + while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + elif event.type == pygame.KEYDOWN: + if event.key == pygame.K_LEFT: + self.x -= 1 + elif event.key == pygame.K_RIGHT: + self.x += 1 + elif event.key == pygame.K_DOWN: + self.y += 1 + elif event.key == pygame.K_UP: + self.y -= 1 + self.move_piece() + self.screen.fill(BLACK) + pygame.draw.rect(self.screen, WHITE, (0, 0, 300, 600), 1) + self.draw_grid() + self.draw_current_piece() + pygame.display.flip() + self.clock.tick(60) + pygame.quit() + +if __name__ == '__main__': + game = Tetris() + game.run() \ No newline at end of file diff --git a/src/dspygen/experiments/self_coding/interview_processing.py b/src/dspygen/experiments/self_coding/interview_processing.py index a450e3b..8e7109e 100644 --- a/src/dspygen/experiments/self_coding/interview_processing.py +++ b/src/dspygen/experiments/self_coding/interview_processing.py @@ -33,7 +33,7 @@ class FeedbackAndRetry(dspy.Signature): def main2(): """Main function""" - init_dspy(Groq, model="mixtral-8x7b-32768") + init_dspy(Groq, max_tokens=1000, model="llama3-70b-8192") # for Groq you must pass the Groq existing model story = ("You are a software engineer preparing for a technical interview. " "You have been given a coding challenge to solve. The challenge involves a NuxtJS frontend with a Convex API backend. ") @@ -67,4 +67,5 @@ def main(): if __name__ == '__main__': - main() + #main() + main2() diff --git a/src/dspygen/lm/groq_lm.py b/src/dspygen/lm/groq_lm.py index cee92d9..5410ce3 100644 --- a/src/dspygen/lm/groq_lm.py +++ b/src/dspygen/lm/groq_lm.py @@ -7,14 +7,17 @@ from groq import Groq as GroqClient +default_model = "llama3-70b-8192" class Groq(LM): - def __init__(self, model="mixtral-8x7b-32768", **kwargs): + def __init__(self, model=default_model, **kwargs): #model="mixtral-8x7b-32768", **kwargs): + # TODO - check of passed model is in list of Groq - if not set to some Groq default + #model="llama3-70b-8192" # this is a fix cs somewhere the the model getting still set to openai gpt-3.5-turbo-instruct super().__init__(model) + + print("Groq model used today: " + model) self.provider = "default" - self.history = [] - groq_api_key = os.environ.get("GROQ_API_KEY") if groq_api_key is None: @@ -33,13 +36,13 @@ def __call__(self, prompt, only_completed=True, return_sorted=False, **kwargs): "content": prompt, }, ], - model=self.kwargs.get("model", "mixtral-8x7b-32768"), + model=self.kwargs.get("model", default_model), ) return [chat_completion.choices[0].message.content] def main(): - init_dspy(Groq, model="llama2-70b-4096", max_tokens=2000) + init_dspy(Groq, model=default_model, max_tokens=2000) # init_dspy(max_tokens=2000) pred = dspy.Predict("prompt -> code")(prompt="Fast API CRUD endpoint for fire alarm global IoT network") print(pred.code) diff --git a/src/dspygen/lm/ollama_lm.py b/src/dspygen/lm/ollama_lm.py new file mode 100644 index 0000000..c456547 --- /dev/null +++ b/src/dspygen/lm/ollama_lm.py @@ -0,0 +1,54 @@ +import os +import dspy +from dsp import LM +from dspygen.utils.dspy_tools import init_dspy +from dspy import OllamaLocal as OllamaClient + +# Default model for Ollama +llama3_inst = "llama3:8b-instruct-q5_1" #max_tokens: int = 8000, + +mistral_inst = "mistral:instruct" #max_tokens: int = 30000, + +default_ollama_model = llama3_inst + +class Ollama(LM): + def __init__(self, model=default_ollama_model, **kwargs): + super().__init__(model) + + # Print which model is being used + print("Ollama model used today: " + model) + self.provider = "default" + self.history = [] + + # Initialize the Ollama client with the API key + self.client = OllamaClient(model=model, timeout_s = 300) + + def basic_request(self, prompt, **kwargs): + # Request a chat completion with the given prompt and model + chat_completion = self.client.request(prompt, **kwargs) + + # Check for choices and extract the content from 'message' + if 'choices' in chat_completion: + # Return the 'content' of the first choice + return [choice['message']['content'] for choice in chat_completion['choices']] + else: + raise ValueError("Expected 'choices' key not found in response") + + def __call__(self, prompt, **kwargs): + return self.basic_request(prompt, **kwargs) + + +# Main function to initialize dspy with Ollama and run a prediction +def main(): + # Initialize dspy with the Ollama class and specified model + init_dspy(Ollama, model=default_ollama_model, max_tokens=8000) + + # Generate prediction for a specific prompt + pred = dspy.Predict("prompt -> code")(prompt="Fast API CRUD endpoint for fire alarm global IoT network") + + # Print the generated code + print(pred.code) + +# Entry point of the script +if __name__ == '__main__': + main() diff --git a/src/dspygen/modules/blog_module.py b/src/dspygen/modules/blog_module.py index 39ec0e6..8009a58 100644 --- a/src/dspygen/modules/blog_module.py +++ b/src/dspygen/modules/blog_module.py @@ -4,7 +4,11 @@ """ import dspy from typer import Typer +from dspygen.lm.groq_lm import Groq +from dspygen.lm.ollama_lm import Ollama + from dspygen.utils.dspy_tools import init_dspy +from dspygen.writer import data_writer app = Typer() @@ -60,9 +64,13 @@ async def blog_route(data: dict): def main(): - init_dspy() - subject = "" + #init_dspy(Groq, model="llama3-70b-8192", max_tokens=8000) # with Groq you must set the model! + init_dspy(Ollama, model="llama3:8b-instruct-q5_1", max_tokens=8000) # with Ollama you must set the model! -- llama3:70b-instruct ollama run llama3:70b-instruct-q3_K_M + subject = "The Tetris Game, simple but working : in 100 lines" # 300 did not end ok with ollama mistral + #( pls do not run into those issues here: TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType')" print(blog_call(subject=subject)) + # manually created the output to src\dspygen\experiments\blog\Tetris_1.md + #data_writer(data=subject, file_path="Tetris_File.py",) if __name__ == "__main__": diff --git a/src/dspygen/modules/json_module.py b/src/dspygen/modules/json_module.py index b5bb8ee..41b940c 100644 --- a/src/dspygen/modules/json_module.py +++ b/src/dspygen/modules/json_module.py @@ -107,7 +107,7 @@ def json_call(schema, text): def main(): from dspygen.lm.groq_lm import Groq # init_dspy(Groq, 1000, "mixtral-8x7b-32768") - init_dspy(Groq, 1000, "llama2-70b-4096") + init_dspy(Groq, max_tokens=1000, model= "llama3-70b-8192") # for Groq you must pass an Groq provided model # Create fake data import faker fake = faker.Faker() diff --git a/src/dspygen/utils/dspy_tools.py b/src/dspygen/utils/dspy_tools.py index b998819..6411124 100644 --- a/src/dspygen/utils/dspy_tools.py +++ b/src/dspygen/utils/dspy_tools.py @@ -1,9 +1,9 @@ import dspy -def init_dspy(lm_class=dspy.OpenAI, max_tokens: int = 500, model: str = "gpt-3.5-turbo-instruct", lm_instance=None): +def init_dspy(lm_class=dspy.OpenAI, max_tokens: int = 800, model: str = "gpt-3.5-turbo-instruct", lm_instance=None): if lm_instance: dspy.settings.configure(lm=lm_instance) else: - lm = lm_class(max_tokens=max_tokens, model=model) + lm = lm_class( max_tokens=max_tokens, model=model) dspy.settings.configure(lm=lm)