Package: mirascope
Prompt Engineering focused on:Simplicity through idiomatic syntax→Faster and more reliable releasesSemi-opinionated methods→Reduced complexity that speeds up developmentReliability through validation→More robust applications with fewer bugsHigh-quality up-to-date documentation→Trust and reliabilityDocumentation:https://docs.mirascope.ioSource Code:https://github.com/Mirascope/mirascopeMirascopeis a library purpose-built for Prompt Engineering on top ofPydantic 2.0:Prompts can live as self-contained classes in their own directory:# prompts/book_recommendation.pyfrommirascopeimportPromptclassBookRecommendationPrompt(Prompt):"""I've recently read the following books: {titles_in_quotes}.What should I read next?"""book_titles:list[str]@propertydeftitles_in_quotes(self)->str:"""Returns a comma separated list of book titles each in quotes."""return", ".join([f'"{title}"'fortitleinself.book_titles])Use the prompt anywhere without worrying about internals such as formatting:# script.pyfrompromptsimportBookRecommendationPromptprompt=BookRecommendationPrompt(book_titles=["The Name of the Wind","The Lord of the Rings"])print(str(prompt))#> I've recently read the following books: "The Name of the Wind", "The Lord of the Rings".#  What should I read next?print(prompt.messages)#> [('user', 'I\'ve recently read the following books: "The Name of the Wind", "The Lord of the Rings".\nWhat should I read next?')]Why use Mirascope?Intuitive: Editor support that you expect (e.g.autocompletion,inline errors)Peace of Mind: Pydantic together with our Prompt CLIeliminate prompt-related bugs.Durable: Seamlessly customize and extend functionality.Never maintain a fork.Integration: Easily integrate withJSON Schemaand other tools such asFastAPIConvenience: Tooling that isclean,elegant, anddelightfulthatyou don't need to maintain.Open: Dedication to buildingopen-source toolsyou can use withyour choice of LLM.InstallationInstall Mirascope and start building with LLMs in minutes.pipinstallmirascope🚨 Warning: Strong Opinion 🚨Prompt Engineering is engineering. Beyond basic illustrative examples, prompting quickly becomes complex. Separating prompts from the engineering workflow will only put limitations on what you can build with LLMs. We firmly believe that prompts are far more than "just f-strings" and thus require developer tools that are purpose-built for building these more complex prompts as easily as possible.ExamplesThePromptclass is an extension ofBaseModelwith some additional built-in convenience tooling for writing and formatting your prompts:frommirascopeimportPromptclassGreetingsPrompt(Prompt):"""Hello! It's nice to meet you. My name is {name}. How are you today?"""name:strprompt=GreetingsPrompt(name="William Bakst")print(GreetingsPrompt.template())#> Hello! It's nice to meet you. My name is {name}. How are you today?print(prompt)#> Hello! It's nice to meet you. My name is William Bakst. How are you today?Example ofautocompleteandinline errors:Autocomplete:Inline Errors:You can access the docstring prompt template through theGreetingsPrompt.template()class method, which will automatically take care of removing any additional special characters such as newlines. This enables writing longer prompts that still adhere to the style of your codebase:classGreetingsPrompt(Prompt):"""Salutations! It is a lovely day. Wouldn't you agree? I find that lovely dayssuch as these brighten up my mood quite a bit. I'm rambling...My name is {name}. It's great to meet you."""prompt=GreetingsPrompt(name="William Bakst")print(prompt)#> Salutations, good being! It is a lovely day. Wouldn't you agree? I find that lovely days such as these brighten up my mood quite a bit. I'm rambling...#> My name is William Bakst. It's great to meet you.Thestrmethod is written such that it only formats properties that are templated. This enables writing more complex properties that rely on one or more provided properties:classGreetingsPrompt(Prompt):"""Hi! My name is {formatted_name}. {name_specific_remark}What's your name? Is your name also {name_specific_question}?"""name:str@propertydefformatted_name(self)->str:"""Returns `name` with pizzazz."""returnf"⭐{self.name}⭐"@propertydefname_specific_question(self)->str:"""Returns a question based on `name`."""ifself.name==self.name[::-1]:return"a palindrome"else:return"not a palindrome"@propertydefname_specific_remark(self)->str:"""Returns a remark based on `name`."""returnf"Can you believe my name is{self.name_specific_question}"prompt=GreetingsPrompt(name="Bob")print(prompt)#> Hi! My name is ⭐Bob⭐. Can you believe my name is a palindrome?#> What's your name? Is your name also a palindrome?Notice that writing properties in this way ensures prompt-specific logic is tied directly to the prompt. It happens under the hood from the perspective of the person usingGreetingsPromptclass. Constructing the prompt only requiresname.For writing promps with multiple messages with different roles, you can use themessagesdecorator to extend the functionality of themessagesproperty:frommirascopeimportPrompt,messages@messagesclassGreetingsPrompt(Prompt):"""SYSTEM:You can only speak in haikus.USER:Hello! It's nice to meet you. My name is {name}. How are you today?"""name:strprompt=GreetingsPrompt(name="William Bakst")print(GreetingsPrompt.template())#> SYSTEM: You can only speak in haikus.#> USER: Hello! It's nice to meet you. My name is {name}. How are you today?print(prompt)#> SYSTEM: You can only speak in haikus.#> USER: Hello! It's nice to meet you. My name is William Bakst. How are you today?print(prompt.messages)#> [('system', 'You can only speak in haikus.'), ('user', "Hello! It's nice to meet you. My name is William Bakst. How are you today?")]The basePromptclass without the decorator will still have themessagesattribute, but it will return a single user message in the list.Remember: this is pythonThere's nothing stopping you from doing things however you'd like. For example, reclaim the docstring:frommirascopeimportPromptTEMPLATE="""This is now my prompt template for{topic}"""classNormalDocstringPrompt(Prompt):"""This is now just a normal docstring."""topic:strdeftemplate(self)->str:"""Returns this prompt's template."""returnTEMPLATEprompt=NormalDocstringPrompt(topic="prompts")print(prompt)#> This is now my prompt template for promptSince the `Prompt`'s `str` method uses template, the above will work as expected.Additional ExamplesIf you're using our LLM convenience wrappers, you'll need to get an API key to use the model of your choice.OpenAI Account Setupwill walk you through how to get your OpenAI API Key. You can follow similar steps for the provider of your choice (e.g.AnyscaleorTogether), or you can use a raw client for non-OpenAI models, likeMistral.Because thePromptclass is built on top ofBaseModel, prompts easily integrate with tools likeFastAPI:FastAPI ExampleimportosfromfastapiimportFastAPIfrommirascopeimportOpenAIChatfrompromptsimportGreetingsPromptapp=FastAPI()@app.post("/greetings")defroot(prompt:GreetingsPrompt)->str:"""Returns an AI generated greeting."""model=OpenAIChat(api_key=os.environ["OPENAI_API_KEY"])returnstr(model.create(prompt))You can also use thePromptclass with whichever LLM you want to use:Mistral Exampleimportosfrommistralai.clientimportMistralClientfrommistralai.models.chat_completionimportChatMessagefrompromptsimportGreetingsPromptclient=MistralClient(api_key=os.environ["MISTRAL_API_KEY"])prompt=GreetingsPrompt(name="William Bakst")messages=[ChatMessage(role=role,content=content)forrole,contentinprompt.messages]# No streamingchat_response=client.chat(model="mistral-tiny",messages=messages,)OpenAI ExampleimportosfromopenaiimportOpenAIfrompromptsimportGreetingsPromptclient=OpenAI(api_key=os.environ["OPENAI_API_KEY"])prompt=GreetingsPrompt(name="William Bakst")messages=[{"role":role,"content":content}forrole,contentinprompt.messages]completion=client.chat.completions.create(model="gpt-3.5-turbo",messages=messages,)Dive DeeperCheck out all of the possibilities of what you can do withPydantic Prompts.Take a look at ourMirascope CLIfor a semi-opinionated prompt mangement system.For convenience, we provide some wrappers around the OpenAI Python SDK for common tasks such as creation, streaming, tools, and extraction. We've found that the concepts covered inLLM Convenience Wrappersmake building LLM-powered apps delightful.TheAPI Referencecontains full details on all classes, methods, functions, etc.You can take a look atcode examplesin the repo that demonstrate how to use the library effectively.ContributingMirascope welcomes contributions from the community! See thecontribution guidefor more information on the development workflow. For bugs and feature requests, visit ourGitHub Issuesand check out ourtemplates.How To HelpAny and all help is greatly appreciated! Check out our page onhow you can help.Roadmap (What's on our mind)Better DX for Mirascope CLI (e.g. autocomplete)Functions as OpenAI toolsBetter chat historyTesting for promptsLogging prompts and their responsesEvaluating prompt qualityRAGAgentsVersioningMirascope usesSemantic Versioning.LicenseThis project is licensed under the terms of theMIT License.