{"id":15064549,"url":"https://github.com/hiremav/spectre","last_synced_at":"2025-04-10T12:22:13.591Z","repository":{"id":257456261,"uuid":"848277988","full_name":"hiremav/spectre","owner":"hiremav","description":"Spectre is a Ruby gem that makes it easy to AI-enable your Ruby on Rails application.","archived":false,"fork":false,"pushed_at":"2025-01-30T13:27:39.000Z","size":148,"stargazers_count":23,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-24T11:08:40.789Z","etag":null,"topics":["ai","llm","mongodb","ruby","ruby-on-rails"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hiremav.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-27T13:21:59.000Z","updated_at":"2025-03-03T14:50:39.000Z","dependencies_parsed_at":"2024-09-16T22:05:19.825Z","dependency_job_id":"48620fb5-dfc7-4863-b4b6-9eea8e262e88","html_url":"https://github.com/hiremav/spectre","commit_stats":{"total_commits":6,"total_committers":2,"mean_commits":3.0,"dds":"0.33333333333333337","last_synced_commit":"9ad65aa0917b3cd40dfbda3124af4f4a68ac7233"},"previous_names":["hiremav/spectre"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiremav%2Fspectre","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiremav%2Fspectre/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiremav%2Fspectre/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hiremav%2Fspectre/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hiremav","download_url":"https://codeload.github.com/hiremav/spectre/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248217115,"owners_count":21066633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","llm","mongodb","ruby","ruby-on-rails"],"created_at":"2024-09-25T00:20:27.535Z","updated_at":"2025-04-10T12:22:13.577Z","avatar_url":"https://github.com/hiremav.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# \u003cimg src='logo.svg' height='120' alt='Spectre Logo' /\u003e\n\n[![Gem Version](https://badge.fury.io/rb/spectre_ai.svg)](https://badge.fury.io/rb/spectre_ai)\n\n**Spectre** is a Ruby gem that makes it easy to AI-enable your Ruby on Rails application. Currently, Spectre focuses on helping developers create embeddings, perform vector-based searches, create chat completions, and manage dynamic prompts — ideal for applications that are featuring RAG (Retrieval-Augmented Generation), chatbots and dynamic prompts.\n\n## Compatibility\n\n| Feature                 | Compatibility  |\n|-------------------------|----------------|\n| Foundation Models (LLM) | OpenAI, Ollama |\n| Embeddings              | OpenAI, Ollama |\n| Vector Searching        | MongoDB Atlas  |\n| Prompt Templates        | ✅            |\n\n**💡 Note:** We will first prioritize adding support for additional foundation models (Claude, Cohere, etc.), then look to add support for more vector databases (Pgvector, Pinecone, etc.). If you're looking for something a bit more extensible, we highly recommend checking out [langchainrb](https://github.com/patterns-ai-core/langchainrb).\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'spectre_ai'\n```\n\nAnd then execute:\n\n```bash\nbundle install\n```\n\nOr install it yourself as:\n\n```bash\ngem install spectre_ai\n```\n\n## Usage\n\n### 🔧 Configuration\n\nFirst, you’ll need to generate the initializer. Run the following command to create the initializer:\n\n```bash\nrails generate spectre:install\n```\n\nThis will create a file at `config/initializers/spectre.rb`, where you can set your llm provider and configure the provider-specific settings.\n\n```ruby\nSpectre.setup do |config|\n  config.default_llm_provider = :openai\n\n  config.openai do |openai|\n    openai.api_key = ENV['OPENAI_API_KEY']\n  end\n\n  config.ollama do |ollama|\n    ollama.host = ENV['OLLAMA_HOST']\n    ollama.api_key = ENV['OLLAMA_API_KEY']\n  end\nend\n```\n\n### 📡 Embeddings \u0026 Vector Search\n\n#### For Embedding\n\nTo use Spectre for generating embeddings in your Rails model, follow these steps:\n\n1. Include the Spectre module.\n2. Declare the model as embeddable.\n3. Define the embeddable fields.\n\nHere is an example of how to set this up in a model:\n\n```ruby\nclass Model\n  include Mongoid::Document\n  include Spectre\n\n  spectre :embeddable\n  embeddable_field :message, :response, :category\nend\n```\n\n#### For Vector Searching (MongoDB Only)\n\n**Note:** Currently, the `Searchable` module is designed to work exclusively with Mongoid models. If you attempt to include it in a non-Mongoid model, an error will be raised. This ensures that vector-based searches, which rely on MongoDB's specific features, are only used in appropriate contexts.\n\nTo enable vector-based search in your Rails model:\n\n1. Include the Spectre module.\n2. Declare the model as searchable.\n3. Configure search parameters.\n\nUse the following methods to configure the search path, index, and result fields:\n\n- **configure_spectre_search_path:** The path where the embeddings are stored.\n- **configure_spectre_search_index:** The index used for the vector search.\n- **configure_spectre_result_fields:** The fields to include in the search results.\n\nHere is an example of how to set this up in a model:\n\n```ruby\nclass Model\n  include Mongoid::Document\n  include Spectre\n\n  spectre :searchable\n  configure_spectre_search_path 'embedding'\n  configure_spectre_search_index 'vector_index'\n  configure_spectre_result_fields({ \"message\" =\u003e 1, \"response\" =\u003e 1 })\nend\n```\n\n### 3. Create Embeddings\n\n**Create Embedding for a Single Record**\n\nTo create an embedding for a single record, you can call the `embed!` method on the instance record:\n\n```ruby\nrecord = Model.find(some_id)\nrecord.embed!\n```\n\nThis will create the embedding and store it in the specified embedding field, along with the timestamp in the `embedded_at` field.\n\n**Create Embeddings for Multiple Records**\n\nTo create embeddings for multiple records at once, use the `embed_all!` method:\n\n```ruby\nModel.embed_all!(\n  scope: -\u003e { where(:response.exists =\u003e true, :response.ne =\u003e nil) },\n  validation: -\u003e(record) { !record.response.blank? }\n)\n```\n\nThis method will create embeddings for all records that match the given scope and validation criteria. The method will also print the number of successful and failed embeddings to the console.\n\n**Directly Create Embeddings Using `Spectre.provider_module::Embeddings.create`**\n\nIf you need to create an embedding directly without using the model integration, you can use the `Spectre.provider_module::Embeddings.create` method. This can be useful if you want to create embeddings for custom text outside of your models. For example, with OpenAI:\n\n```ruby\nSpectre.provider_module::Embeddings.create(\"Your text here\")\n```\n\nThis method sends the text to OpenAI’s API and returns the embedding vector. You can optionally specify a different model by passing it as an argument:\n\n```ruby\nSpectre.provider_module::Embeddings.create(\"Your text here\", model: \"text-embedding-ada-002\")\n```\n\n**NOTE:** Different providers have different available args for the `create` method. Please refer to the provider-specific documentation for more details.\n\n### 4. Performing Vector-Based Searches\n\nOnce your model is configured as searchable, you can perform vector-based searches on the stored embeddings:\n\n```ruby\nModel.vector_search('Your search query', custom_result_fields: { \"response\" =\u003e 1 }, additional_scopes: [{ \"$match\" =\u003e { \"category\" =\u003e \"science\" } }])\n```\n\nThis method will:\n\n- **Embed the Search Query:** Uses the configured LLM provider to embed the search query.  \n  **Note:** If your text is already embedded, you can pass the embedding (as an array), and it will perform just the search.\n\n- **Perform Vector-Based Search:** Searches the embeddings stored in the specified `search_path`.\n\n- **Return Matching Records:** Provides the matching records with the specified `result_fields` and their `vectorSearchScore`.\n\n**Keyword Arguments:**\n\n- **custom_result_fields:** Limit the fields returned in the search results.\n- **additional_scopes:** Apply additional MongoDB filters to the search results.\n\n### 💬 Chat Completions\n\nSpectre provides an interface to create chat completions using your configured LLM provider, allowing you to create dynamic responses, messages, or other forms of text.\n\n**Basic Completion Example**\n\nTo create a simple chat completion, use the `Spectre.provider_module::Completions.create` method. You can provide a user prompt and an optional system prompt to guide the response:\n\n```ruby\nmessages = [\n        { role: 'system', content: \"You are a funny assistant.\" },\n        { role: 'user', content: \"Tell me a joke.\" }\n]\n\nSpectre.provider_module::Completions.create(messages: messages)\n```\n\nThis sends the request to the LLM provider’s API and returns the chat completion.\n\n**Customizing the Completion**\n\nYou can customize the behavior by specifying additional parameters such as the model, any tools needed for function calls:\n\n```ruby\nmessages = [\n        { role: 'system', content: \"You are a funny assistant.\" },\n        { role: 'user', content: \"Tell me a joke.\" },\n        { role: 'assistant', content: \"Sure, here's a joke!\" }\n]\n\nSpectre.provider_module::Completions.create(\n        messages: messages,\n        model: \"gpt-4\",\n        openai: { max_tokens: 50 }\n)\n\n```\n\n**Using a JSON Schema for Structured Output**\n\nFor cases where you need structured output (e.g., for returning specific fields or formatted responses), you can pass a `json_schema` parameter. The schema ensures that the completion conforms to a predefined structure:\n\n```ruby\njson_schema = {\n  name: \"completion_response\",\n  schema: {\n    type: \"object\",\n    properties: {\n      response: { type: \"string\" },\n      final_answer: { type: \"string\" }\n    },\n    required: [\"response\", \"final_answer\"],\n    additionalProperties: false\n  }\n}\n\nmessages = [\n  { role: 'system', content: \"You are a knowledgeable assistant.\" },\n  { role: 'user', content: \"What is the capital of France?\" }\n]\n\nSpectre.provider_module::Completions.create(\n  messages: messages,\n  json_schema: json_schema\n)\n\n```\n\nThis structured format guarantees that the response adheres to the schema you’ve provided, ensuring more predictable and controlled results.\n\n**NOTE:** The JSON schema is different for each provider. OpenAI uses [JSON Schema](https://json-schema.org/overview/what-is-jsonschema.html), where you can specify the name of schema and schema itself. Ollama uses just plain JSON object. \nBut you can provide OpenAI's schema to Ollama as well. We just convert it to Ollama's format.\n\n⚙️ Function Calling (Tool Use)\n\nYou can incorporate tools (function calls) in your completion to handle more complex interactions such as fetching external information via API or performing calculations. Define tools using the function call format and include them in the request:\n\n```ruby\ntools = [\n  {\n    type: \"function\",\n    function: {\n      name: \"get_delivery_date\",\n      description: \"Get the delivery date for a customer's order.\",\n      parameters: {\n        type: \"object\",\n        properties: {\n          order_id: { type: \"string\", description: \"The customer's order ID.\" }\n        },\n        required: [\"order_id\"],\n        additionalProperties: false\n      }\n    }\n  }\n]\n\nmessages = [\n  { role: 'system', content: \"You are a helpful customer support assistant.\" },\n  { role: 'user', content: \"Can you tell me the delivery date for my order?\" }\n]\n\nSpectre.provider_module::Completions.create(\n  messages: messages,\n  tools: tools\n)\n```\n\nThis setup allows the model to call specific tools (or functions) based on the user's input. The model can then generate a tool call to get necessary information and integrate it into the conversation.\n\n**Handling Responses from Completions with Tools**\n\nWhen tools (function calls) are included in a completion request, the response might include `tool_calls` with relevant details for executing the function.\n\nHere’s an example of how the response might look when a tool call is made:\n\n```ruby\nresponse = Spectre.provider_module::Completions.create(\n  messages: messages,\n  tools: tools\n)\n\n# Sample response structure when a tool call is triggered:\n# {\n#   :tool_calls=\u003e[{\n#     \"id\" =\u003e \"call_gqvSz1JTDfUyky7ghqY1wMoy\",\n#     \"type\" =\u003e \"function\",\n#     \"function\" =\u003e {\n#       \"name\" =\u003e \"get_lead_count\",\n#       \"arguments\" =\u003e \"{\\\"account_id\\\":\\\"acc_12312\\\"}\"\n#     }\n#   }],\n#   :content =\u003e nil\n# }\n\nif response[:tool_calls]\n  tool_call = response[:tool_calls].first\n\n  # You can now perform the function using the provided data\n  # For example, get the lead count by account_id\n  account_id = JSON.parse(tool_call['function']['arguments'])['account_id']\n  lead_count = get_lead_count(account_id) # Assuming you have a method for this\n\n  # Respond back with the function result\n  completion_response = Spectre.provider_module::Completions.create(\n    messages: [\n      { role: 'assistant', content: \"There are #{lead_count} leads for account #{account_id}.\" }\n    ]\n  )\nelse\n  puts \"Model response: #{response[:content]}\"\nend\n```\n\n**NOTE:** Completions class also supports different `**args` for different providers. Please refer to the provider-specific documentation for more details.\n\n### 🎭 Dynamic Prompt Rendering\n\nSpectre provides a system for creating dynamic prompts based on templates. You can define reusable prompt templates and render them with different parameters in your Rails app (think Ruby on Rails view partials).\n\n**Example Directory Structure for Prompts**\n\nCreate a folder structure in your app to hold the prompt templates:\n\n```\napp/spectre/prompts/\n└── rag/\n    ├── system.yml.erb\n    └── user.yml.erb\n```\n\nEach `.yml.erb` file can contain dynamic content and be customized with embedded Ruby (ERB).\n\n**Example Prompt Templates**\n\n- **`system.yml.erb`:**\n\n  ```yaml\n  system: |\n    You are a helpful assistant designed to provide answers based on specific documents and context provided to you.\n    Follow these guidelines:\n    1. Only provide answers based on the context provided.\n    2. Be polite and concise.\n  ```\n\n- **`user.yml.erb`:**\n\n  ```yaml\n  user: |\n    User's query: \u003c%= @query %\u003e\n    Context: \u003c%= @objects.join(\", \") %\u003e\n  ```\n\n**Rendering Prompts**\n\nYou can render prompts in your Rails application using the `Spectre::Prompt.render` method, which loads and renders the specified prompt template:\n\n```ruby\n# Render a system prompt\nSpectre::Prompt.render(template: 'rag/system')\n\n# Render a user prompt with local variables\nSpectre::Prompt.render(\n  template: 'rag/user',\n  locals: {\n    query: query,\n    objects: objects\n  }\n)\n```\n\n- **`template`:** The path to the prompt template file (e.g., `rag/system`).\n- **`locals`:** A hash of variables to be used inside the ERB template.\n\n**Using Nested Templates for Prompts**\n\nSpectre's `Prompt` class now supports rendering templates from nested directories. This allows you to better organize your prompt files in a structured folder hierarchy.\n\nYou can organize your prompt templates in subfolders. For instance, you can have the following structure:\n\n```\napp/\n  spectre/\n    prompts/\n      rag/\n        system.yml.erb\n        user.yml.erb\n      classification/\n        intent/\n          system.yml.erb\n          user.yml.erb\n        entity/\n          system.yml.erb\n          user.yml.erb\n```\n\nTo render a prompt from a nested folder, simply pass the full path to the `template` argument:\n\n```ruby\n# Rendering from a nested folder\nSpectre::Prompt.render(template: 'classification/intent/user', locals: { query: 'What is AI?' })\n```\n\nThis allows for more flexibility when organizing your prompt files, particularly when dealing with complex scenarios or multiple prompt categories.\n\n**Combining Completions with Prompts**\n\nYou can also combine completions and prompts like so:\n\n```ruby\nSpectre.provider_module::Completions.create(\n  messages: [\n    { role: 'system', content: Spectre::Prompt.render(template: 'rag/system') },\n    { role: 'user', content: Spectre::Prompt.render(template: 'rag/user', locals: { query: @query, user: @user }) }\n  ]\n)\n\n```\n\n## 📜 Contributing\n\nBug reports and pull requests are welcome on GitHub at [https://github.com/hiremav/spectre](https://github.com/hiremav/spectre). This project is intended to be a safe, welcoming space for collaboration, and your contributions are greatly appreciated!\n\n1. **Fork** the repository.\n2. **Create** a new feature branch (`git checkout -b my-new-feature`).\n3. **Commit** your changes (`git commit -am 'Add some feature'`).\n4. **Push** the branch (`git push origin my-new-feature`).\n5. **Create** a pull request.\n\n## 📜 License\n\nThis gem is available as open source under the terms of the MIT License.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhiremav%2Fspectre","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhiremav%2Fspectre","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhiremav%2Fspectre/lists"}