your_ai_insight 1.0.10 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/services/your_ai_insight/claude_service.rb +24 -19
- data/lib/your_ai_insight/configuration.rb +21 -16
- data/lib/your_ai_insight/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d1204c7328e49de4459471b39022f9d2482ddebc0e72f26774cf889d1feb7fc
|
|
4
|
+
data.tar.gz: c01acdcda667e9bf44ba364d5e9c48138da79ae42375c8a327c65a17c6d1b4c6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 53ee1ee3ab937cd1ec3436f44af642a687e223b5c62ba05d7da3d43a0f8f2cc8fbba7c6e577bb9a240f31a19394b3ffa6d78880686d75f0c9d8d5f2c0adca541
|
|
7
|
+
data.tar.gz: 401fba06aff405ab6d07c08aa547b3514ae365fd155add6b61b535d835e22b2254210ff2706e84a8b15abff2a15868274b7035574351f0a04fc801bea8776d1d
|
|
@@ -3,7 +3,6 @@ require "httparty"
|
|
|
3
3
|
module YourAiInsight
|
|
4
4
|
class ClaudeService
|
|
5
5
|
include HTTParty
|
|
6
|
-
base_uri "https://api.anthropic.com"
|
|
7
6
|
|
|
8
7
|
SYSTEM_PROMPT = <<~PROMPT.freeze
|
|
9
8
|
You are an expert facility management analyst for AllPro IFM.
|
|
@@ -32,11 +31,14 @@ module YourAiInsight
|
|
|
32
31
|
PROMPT
|
|
33
32
|
|
|
34
33
|
def initialize
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@
|
|
34
|
+
cfg = YourAiInsight.config
|
|
35
|
+
@api_key = cfg.openrouter_api_key ||
|
|
36
|
+
ENV["OPENROUTER_API_KEY"] ||
|
|
37
|
+
raise(ConfigurationError, "Set config.openrouter_api_key or ENV['OPENROUTER_API_KEY']")
|
|
38
|
+
@model = cfg.openrouter_model
|
|
39
|
+
@max_tokens = cfg.max_tokens
|
|
40
|
+
@site_url = cfg.openrouter_site_url
|
|
41
|
+
@site_name = cfg.openrouter_site_name
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
# Free-form chat with optional live data context injected
|
|
@@ -57,18 +59,22 @@ module YourAiInsight
|
|
|
57
59
|
private
|
|
58
60
|
|
|
59
61
|
def call_api(messages)
|
|
62
|
+
headers = {
|
|
63
|
+
"Authorization" => "Bearer #{@api_key}",
|
|
64
|
+
"Content-Type" => "application/json"
|
|
65
|
+
}
|
|
66
|
+
headers["HTTP-Referer"] = @site_url if @site_url.present?
|
|
67
|
+
headers["X-Title"] = @site_name if @site_name.present?
|
|
68
|
+
|
|
69
|
+
full_messages = [{ role: "system", content: SYSTEM_PROMPT }] + messages
|
|
70
|
+
|
|
60
71
|
response = self.class.post(
|
|
61
|
-
"/v1/
|
|
62
|
-
headers:
|
|
63
|
-
"x-api-key" => @api_key,
|
|
64
|
-
"anthropic-version" => "2023-06-01",
|
|
65
|
-
"content-type" => "application/json"
|
|
66
|
-
},
|
|
72
|
+
"https://openrouter.ai/api/v1/chat/completions",
|
|
73
|
+
headers: headers,
|
|
67
74
|
body: {
|
|
68
75
|
model: @model,
|
|
69
76
|
max_tokens: @max_tokens,
|
|
70
|
-
|
|
71
|
-
messages: messages
|
|
77
|
+
messages: full_messages
|
|
72
78
|
}.to_json,
|
|
73
79
|
timeout: 90
|
|
74
80
|
)
|
|
@@ -76,17 +82,16 @@ module YourAiInsight
|
|
|
76
82
|
body = JSON.parse(response.body)
|
|
77
83
|
|
|
78
84
|
unless response.success?
|
|
79
|
-
raise ApiError, "
|
|
85
|
+
raise ApiError, "OpenRouter error (HTTP #{response.code}): #{body.dig('error', 'message')}"
|
|
80
86
|
end
|
|
81
87
|
|
|
82
|
-
body.dig("
|
|
83
|
-
raise(ApiError, "Empty response from
|
|
88
|
+
body.dig("choices", 0, "message", "content").presence ||
|
|
89
|
+
raise(ApiError, "Empty response from OpenRouter")
|
|
84
90
|
|
|
85
91
|
rescue HTTParty::Error, Timeout::Error => e
|
|
86
|
-
raise ApiError, "Network error reaching
|
|
92
|
+
raise ApiError, "Network error reaching OpenRouter: #{e.message}"
|
|
87
93
|
end
|
|
88
94
|
|
|
89
|
-
# Injects a live snapshot into the chat prompt
|
|
90
95
|
def context_block(ctx)
|
|
91
96
|
return nil if ctx.blank?
|
|
92
97
|
lines = ["## Live Facility Snapshot"]
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
module YourAiInsight
|
|
2
2
|
class Configuration
|
|
3
|
-
# ──
|
|
4
|
-
attr_accessor :
|
|
3
|
+
# ── OpenRouter (required) ──────────────────────────────────────────────────
|
|
4
|
+
attr_accessor :openrouter_api_key
|
|
5
|
+
# See https://openrouter.ai/models?q=free for free models
|
|
6
|
+
attr_accessor :openrouter_model # default: meta-llama/llama-3.3-70b-instruct:free
|
|
7
|
+
attr_accessor :openrouter_site_url # optional — sent as HTTP-Referer
|
|
8
|
+
attr_accessor :openrouter_site_name # optional — sent as X-Title
|
|
5
9
|
|
|
6
|
-
# ──
|
|
7
|
-
attr_accessor :
|
|
8
|
-
attr_accessor :max_tokens # default: 2048
|
|
10
|
+
# ── Response tuning ────────────────────────────────────────────────────────
|
|
11
|
+
attr_accessor :max_tokens # default: 2048
|
|
9
12
|
|
|
10
|
-
# ── Multi-tenancy: scope data to current user's locations/customers
|
|
13
|
+
# ── Multi-tenancy: scope data to current user's locations/customers ────────
|
|
11
14
|
# Proc receives current_user and returns an Array of location_ids, or nil for all.
|
|
12
15
|
# Example: config.location_scope = ->(user) { user.locations.pluck(:id) }
|
|
13
16
|
attr_accessor :location_scope
|
|
@@ -87,11 +90,13 @@ module YourAiInsight
|
|
|
87
90
|
attr_accessor :sub_contractors_table # "sub_contractors"
|
|
88
91
|
|
|
89
92
|
def initialize
|
|
90
|
-
@
|
|
91
|
-
@
|
|
92
|
-
@
|
|
93
|
-
@
|
|
94
|
-
@
|
|
93
|
+
@openrouter_model = "meta-llama/llama-3.3-70b-instruct:free"
|
|
94
|
+
@openrouter_site_url = nil
|
|
95
|
+
@openrouter_site_name = nil
|
|
96
|
+
@max_tokens = 2048
|
|
97
|
+
@company_name = "AllPro IFM"
|
|
98
|
+
@logo_url = nil
|
|
99
|
+
@default_recipients = []
|
|
95
100
|
|
|
96
101
|
# jobs
|
|
97
102
|
@jobs_table = "jobs"
|
|
@@ -122,11 +127,11 @@ module YourAiInsight
|
|
|
122
127
|
@bids_task_id_col = "task_id"
|
|
123
128
|
|
|
124
129
|
# expenses
|
|
125
|
-
@expenses_table
|
|
126
|
-
@expenses_budget_col
|
|
127
|
-
@expenses_actual_col
|
|
128
|
-
@expenses_job_id_col
|
|
129
|
-
@expenses_task_id_col= "task_id"
|
|
130
|
+
@expenses_table = "expenses"
|
|
131
|
+
@expenses_budget_col = "budget"
|
|
132
|
+
@expenses_actual_col = "actual"
|
|
133
|
+
@expenses_job_id_col = "job_id"
|
|
134
|
+
@expenses_task_id_col = "task_id"
|
|
130
135
|
|
|
131
136
|
# location_budgets
|
|
132
137
|
@location_budgets_table = "location_budgets"
|