Add tenant to URLs
This commit is contained in:
@@ -3,6 +3,10 @@ class Ai::Tool < RubyLLM::Tool
|
||||
|
||||
private
|
||||
def default_url_options
|
||||
Rails.application.default_url_options
|
||||
Rails.application.default_url_options.merge(
|
||||
script_name: "/#{ApplicationRecord.current_tenant}",
|
||||
host: "fizzy.localhost",
|
||||
port: 3006
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ class Ai::Tool::ListCards < Ai::Tool
|
||||
}
|
||||
}
|
||||
```
|
||||
Each collection object has the following fields:
|
||||
Each card object has the following fields:
|
||||
- id [Integer, not null]
|
||||
- title [String, not null] - The title of the card
|
||||
- status [String, not null] - Enum of "creating", "draft" and "published"
|
||||
@@ -69,8 +69,14 @@ class Ai::Tool::ListCards < Ai::Tool
|
||||
desc: "If provided, will return only card that were last active on or before the given ISO timestamp",
|
||||
required: false
|
||||
|
||||
attr_reader :user
|
||||
|
||||
def initialize(user:)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def execute(**params)
|
||||
cards = Card.all.includes(:stage, :creator, :assignees, :goldness)
|
||||
cards = Card.where(collection: user.collections).includes(:stage, :creator, :assignees, :goldness)
|
||||
|
||||
cards = cards.search(params[:query]) if params[:query].present?
|
||||
cards = cards.golden if params[:golden].present?
|
||||
@@ -99,6 +105,12 @@ class Ai::Tool::ListCards < Ai::Tool
|
||||
|
||||
page = GearedPagination::Recordset.new(cards, ordered_by: { id: :desc }).page(params[:page])
|
||||
|
||||
puts "="*80
|
||||
puts "Account: #{Account.sole.id}"
|
||||
puts "Tenant: #{ApplicationRecord.current_tenant}"
|
||||
puts "URL options: #{default_url_options.inspect}"
|
||||
puts "="*80
|
||||
|
||||
{
|
||||
cards: page.records.map do |card|
|
||||
{
|
||||
@@ -112,7 +124,7 @@ class Ai::Tool::ListCards < Ai::Tool
|
||||
creator: card.creator.as_json(only: [ :id, :name ]),
|
||||
assignees: card.assignees.as_json(only: [ :id, :name ]),
|
||||
description: card.description.to_plain_text.truncate(1000),
|
||||
url: collection_card_path(card.collection, card)
|
||||
url: collection_card_url(card.collection, card)
|
||||
}
|
||||
end,
|
||||
pagination: {
|
||||
|
||||
@@ -24,8 +24,14 @@ class Ai::Tool::ListCollections < Ai::Tool
|
||||
desc: "Which page to return. Leave balnk to get the first page",
|
||||
required: false
|
||||
|
||||
attr_reader :user
|
||||
|
||||
def initialize(user:)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def execute(**params)
|
||||
scope = Collection.all
|
||||
scope = user.collections
|
||||
|
||||
page = GearedPagination::Recordset.new(
|
||||
scope,
|
||||
@@ -37,7 +43,7 @@ class Ai::Tool::ListCollections < Ai::Tool
|
||||
{
|
||||
id: collection.id,
|
||||
name: collection.name,
|
||||
url: collection_path(collection)
|
||||
url: collection_url(collection)
|
||||
}
|
||||
end,
|
||||
pagination: {
|
||||
|
||||
@@ -5,7 +5,7 @@ class Ai::Tool::ListComments < Ai::Tool
|
||||
Responses are JSON objects that look like this:
|
||||
```
|
||||
{
|
||||
"collections": [
|
||||
"comments": [
|
||||
{
|
||||
"id": 3,
|
||||
"card_id": 5,
|
||||
@@ -21,9 +21,18 @@ class Ai::Tool::ListComments < Ai::Tool
|
||||
}
|
||||
}
|
||||
```
|
||||
Each collection object has the following fields:
|
||||
Each comment object has the following fields:
|
||||
- id [Integer, not null]
|
||||
- name [String, not null]
|
||||
- card_id [Integer, not null]
|
||||
- body [String, not null]
|
||||
- created_at [String, not null] ISO8601 formatted timestamp
|
||||
- creator [Object, not null] the User that created the comment
|
||||
- system [Boolean, not null] indicates if the comment was created by the system
|
||||
- reactions [Array]
|
||||
- content [String, not null]
|
||||
- reacter [Object] represents a User
|
||||
- id [Integer, not null]
|
||||
- name [String, not null]
|
||||
MD
|
||||
|
||||
param :page,
|
||||
@@ -38,6 +47,10 @@ class Ai::Tool::ListComments < Ai::Tool
|
||||
type: :integer,
|
||||
desc: "If provided, will return only status changes for the specified card",
|
||||
required: false
|
||||
param :type,
|
||||
type: :string,
|
||||
desc: "If provided, returns either 'user' or 'system' comments, if ommited it returns both",
|
||||
required: false
|
||||
param :created_at_gte,
|
||||
type: :string,
|
||||
desc: "If provided, will return only comments created on or after after the given ISO timestamp",
|
||||
@@ -47,12 +60,25 @@ class Ai::Tool::ListComments < Ai::Tool
|
||||
desc: "If provided, will return only comments created on or before the given ISO timestamp",
|
||||
required: false
|
||||
|
||||
attr_reader :user
|
||||
|
||||
def initialize(user:)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def execute(**params)
|
||||
scope = Comment.all.includes(:card, :creator, reactions: [ :reacter ]).where.not(creator: { role: "system" })
|
||||
cards = Card.where(collection: user.collections)
|
||||
scope = Comment.where(card: cards).includes(:card, :creator, reactions: [ :reacter ])
|
||||
|
||||
scope = scope.search(params[:query]) if params[:query].present?
|
||||
scope = scope.where(card_id: params[:card_id].to_i) if params[:card_id].present?
|
||||
|
||||
if params[:type]&.casecmp?("system")
|
||||
scope = scope.where(creator: { role: "system" })
|
||||
elsif params[:type]&.casecmp?("user")
|
||||
scope = scope.where.not(creator: { role: "system" })
|
||||
end
|
||||
|
||||
if params[:created_at_gte].present?
|
||||
timestamp = Time.iso8601(params[:created_at_gte])
|
||||
scope = scope.where(created_at: timestamp..)
|
||||
@@ -69,19 +95,21 @@ class Ai::Tool::ListComments < Ai::Tool
|
||||
).page(params[:page])
|
||||
|
||||
{
|
||||
collections: page.records.map do |comment|
|
||||
comments: page.records.map do |comment|
|
||||
{
|
||||
id: comment.id,
|
||||
card_id: comment.card_id,
|
||||
body: comment.body.to_plain_text,
|
||||
created_at: comment.created_at.iso8601,
|
||||
creator: comment.creator.as_json(only: [ :id, :name ]),
|
||||
system: comment.creator.system?,
|
||||
reactions: comment.reactions.map do |reaction|
|
||||
{
|
||||
content: reaction.content,
|
||||
reacter: reaction.reacter.as_json(only: [ :id, :name ])
|
||||
}
|
||||
end
|
||||
end,
|
||||
url: collection_card_url(comment.card.collection_id, comment.card, anchor: "comment_#{comment.id}")
|
||||
}
|
||||
end,
|
||||
pagination: {
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
class Ai::Tool::ListStatusChanges < Ai::Tool
|
||||
description <<-MD
|
||||
Lists all status changes accessible by the current user.
|
||||
The response is paginated so you may need to iterate through multiple pages to get the full list.
|
||||
Responses are JSON objects that look like this:
|
||||
```
|
||||
{
|
||||
"collections": [
|
||||
{
|
||||
"id": 3,
|
||||
"card_id": 5,
|
||||
"body": "Jane Doe moved this to Done",
|
||||
"created_at": "2023-10-01T12:00:00Z",
|
||||
"creator": { "id": 2, "name": "Jane Doe" }
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"next_page": "e3c2gh75e4..."
|
||||
}
|
||||
}
|
||||
```
|
||||
Each collection object has the following fields:
|
||||
- id [Integer, not null]
|
||||
- name [String, not null]
|
||||
MD
|
||||
|
||||
param :page,
|
||||
type: :string,
|
||||
desc: "Which page to return. Leave balnk to get the first page",
|
||||
required: false
|
||||
param :query,
|
||||
type: :string,
|
||||
desc: "If provided, will perform a semantinc search by embeddings and return only matching status changes",
|
||||
required: false
|
||||
param :card_id,
|
||||
type: :integer,
|
||||
desc: "If provided, will return only status changes for the specified card",
|
||||
required: false
|
||||
param :created_at_gte,
|
||||
type: :string,
|
||||
desc: "If provided, will return only comments created on or after after the given ISO timestamp",
|
||||
required: false
|
||||
param :created_at_lte,
|
||||
type: :string,
|
||||
desc: "If provided, will return only comments created on or before the given ISO timestamp",
|
||||
required: false
|
||||
|
||||
def execute(**params)
|
||||
scope = Comment.all.includes(:card, :creator).where(creator: { role: "system" })
|
||||
|
||||
scope = scope.search(params[:query]) if params[:query].present?
|
||||
scope = scope.where(card_id: params[:card_id].to_i) if params[:card_id].present?
|
||||
|
||||
if params[:created_at_gte].present?
|
||||
timestamp = Time.iso8601(params[:created_at_gte])
|
||||
scope = scope.where(created_at: timestamp..)
|
||||
end
|
||||
|
||||
if params[:created_at_lte].present?
|
||||
timestamp = Time.iso8601(params[:created_at_lte])
|
||||
scope = scope.where(created_at: ..timestamp)
|
||||
end
|
||||
|
||||
page = GearedPagination::Recordset.new(
|
||||
scope,
|
||||
ordered_by: { created_at: :asc, id: :desc }
|
||||
).page(params[:page])
|
||||
|
||||
{
|
||||
collections: page.records.map do |comment|
|
||||
{
|
||||
id: comment.id,
|
||||
card_id: comment.card_id,
|
||||
body: comment.body.to_plain_text,
|
||||
created_at: comment.created_at.iso8601,
|
||||
creator: comment.creator.as_json(only: [ :id, :name ])
|
||||
}
|
||||
end,
|
||||
pagination: {
|
||||
next_page: page.next_param
|
||||
}
|
||||
}.to_json
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,7 @@ class Ai::Tool::ListUsers < Ai::Tool
|
||||
Responses are JSON objects that look like this:
|
||||
```
|
||||
{
|
||||
"collections": [
|
||||
"users": [
|
||||
{ "id": 3, "name": "John Doe" },
|
||||
{ "id": 4, "name": "Johanna Doe" }
|
||||
],
|
||||
@@ -14,9 +14,11 @@ class Ai::Tool::ListUsers < Ai::Tool
|
||||
}
|
||||
}
|
||||
```
|
||||
Each collection object has the following fields:
|
||||
Each user object has the following fields:
|
||||
- id [Integer, not null]
|
||||
- name [String, not null]
|
||||
- role [String, not null]
|
||||
- url [String, not null]
|
||||
MD
|
||||
|
||||
param :page,
|
||||
@@ -28,6 +30,12 @@ class Ai::Tool::ListUsers < Ai::Tool
|
||||
desc: "If provided, will return only the users with the given IDs (comma-separated)",
|
||||
required: false
|
||||
|
||||
attr_reader :user
|
||||
|
||||
def initialize(user:)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def execute(**params)
|
||||
scope = User.all
|
||||
|
||||
@@ -39,11 +47,12 @@ class Ai::Tool::ListUsers < Ai::Tool
|
||||
).page(params[:page])
|
||||
|
||||
{
|
||||
collections: page.records.map do |user|
|
||||
users: page.records.map do |user|
|
||||
{
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
url: user_path(user)
|
||||
role: user.role,
|
||||
url: user_url(user)
|
||||
}
|
||||
end,
|
||||
pagination: {
|
||||
|
||||
@@ -25,7 +25,7 @@ class Conversation::Message::ResponseGenerator
|
||||
- Respond in **Markdown**
|
||||
- Always include links to relevant cards, collections, comments, or users
|
||||
|
||||
Remember, you're here to help — not to anticipate.
|
||||
You're here to help — not to anticipate.
|
||||
PROMPT
|
||||
|
||||
attr_reader :message, :prompt, :llm_model
|
||||
@@ -62,11 +62,10 @@ class Conversation::Message::ResponseGenerator
|
||||
|
||||
def llm
|
||||
RubyLLM.chat(model: llm_model).tap do |chat|
|
||||
chat.with_tool(Ai::Tool::ListCards.new)
|
||||
chat.with_tool(Ai::Tool::ListCollections.new)
|
||||
chat.with_tool(Ai::Tool::ListComments.new)
|
||||
chat.with_tool(Ai::Tool::ListStatusChanges.new)
|
||||
chat.with_tool(Ai::Tool::ListUsers.new)
|
||||
chat.with_tool(Ai::Tool::ListCards.new(user: message.owner))
|
||||
chat.with_tool(Ai::Tool::ListCollections.new(user: message.owner))
|
||||
chat.with_tool(Ai::Tool::ListComments.new(user: message.owner))
|
||||
chat.with_tool(Ai::Tool::ListUsers.new(user: message.owner))
|
||||
|
||||
chat.reset_messages!
|
||||
|
||||
|
||||
Reference in New Issue
Block a user