your_ai_insight 1.0.11 → 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 +12 -56
- data/lib/your_ai_insight/configuration.rb +16 -25
- 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
|
|
@@ -31,24 +31,14 @@ module YourAiInsight
|
|
|
31
31
|
PROMPT
|
|
32
32
|
|
|
33
33
|
def initialize
|
|
34
|
-
cfg
|
|
35
|
-
@
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
raise(ConfigurationError, "Set config.openrouter_api_key or ENV['OPENROUTER_API_KEY']")
|
|
43
|
-
@model = cfg.openrouter_model
|
|
44
|
-
@site_url = cfg.openrouter_site_url
|
|
45
|
-
@site_name = cfg.openrouter_site_name
|
|
46
|
-
else
|
|
47
|
-
@api_key = cfg.anthropic_api_key ||
|
|
48
|
-
ENV["ANTHROPIC_API_KEY"] ||
|
|
49
|
-
raise(ConfigurationError, "Set config.anthropic_api_key or ENV['ANTHROPIC_API_KEY']")
|
|
50
|
-
@model = cfg.claude_model
|
|
51
|
-
end
|
|
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
|
|
52
42
|
end
|
|
53
43
|
|
|
54
44
|
# Free-form chat with optional live data context injected
|
|
@@ -69,39 +59,6 @@ module YourAiInsight
|
|
|
69
59
|
private
|
|
70
60
|
|
|
71
61
|
def call_api(messages)
|
|
72
|
-
@provider == :openrouter ? call_openrouter(messages) : call_claude(messages)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def call_claude(messages)
|
|
76
|
-
response = self.class.post(
|
|
77
|
-
"https://api.anthropic.com/v1/messages",
|
|
78
|
-
headers: {
|
|
79
|
-
"x-api-key" => @api_key,
|
|
80
|
-
"anthropic-version" => "2023-06-01",
|
|
81
|
-
"content-type" => "application/json"
|
|
82
|
-
},
|
|
83
|
-
body: {
|
|
84
|
-
model: @model,
|
|
85
|
-
max_tokens: @max_tokens,
|
|
86
|
-
system: SYSTEM_PROMPT,
|
|
87
|
-
messages: messages
|
|
88
|
-
}.to_json,
|
|
89
|
-
timeout: 90
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
body = JSON.parse(response.body)
|
|
93
|
-
unless response.success?
|
|
94
|
-
raise ApiError, "Claude API error (HTTP #{response.code}): #{body.dig('error', 'message')}"
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
body.dig("content", 0, "text").presence ||
|
|
98
|
-
raise(ApiError, "Empty response from Claude API")
|
|
99
|
-
|
|
100
|
-
rescue HTTParty::Error, Timeout::Error => e
|
|
101
|
-
raise ApiError, "Network error reaching Claude API: #{e.message}"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def call_openrouter(messages)
|
|
105
62
|
headers = {
|
|
106
63
|
"Authorization" => "Bearer #{@api_key}",
|
|
107
64
|
"Content-Type" => "application/json"
|
|
@@ -109,7 +66,6 @@ module YourAiInsight
|
|
|
109
66
|
headers["HTTP-Referer"] = @site_url if @site_url.present?
|
|
110
67
|
headers["X-Title"] = @site_name if @site_name.present?
|
|
111
68
|
|
|
112
|
-
# Prepend system prompt as a system message (OpenAI-compatible format)
|
|
113
69
|
full_messages = [{ role: "system", content: SYSTEM_PROMPT }] + messages
|
|
114
70
|
|
|
115
71
|
response = self.class.post(
|
|
@@ -124,18 +80,18 @@ module YourAiInsight
|
|
|
124
80
|
)
|
|
125
81
|
|
|
126
82
|
body = JSON.parse(response.body)
|
|
83
|
+
|
|
127
84
|
unless response.success?
|
|
128
|
-
raise ApiError, "OpenRouter
|
|
85
|
+
raise ApiError, "OpenRouter error (HTTP #{response.code}): #{body.dig('error', 'message')}"
|
|
129
86
|
end
|
|
130
87
|
|
|
131
88
|
body.dig("choices", 0, "message", "content").presence ||
|
|
132
|
-
raise(ApiError, "Empty response from OpenRouter
|
|
89
|
+
raise(ApiError, "Empty response from OpenRouter")
|
|
133
90
|
|
|
134
91
|
rescue HTTParty::Error, Timeout::Error => e
|
|
135
|
-
raise ApiError, "Network error reaching OpenRouter
|
|
92
|
+
raise ApiError, "Network error reaching OpenRouter: #{e.message}"
|
|
136
93
|
end
|
|
137
94
|
|
|
138
|
-
# Injects a live snapshot into the chat prompt
|
|
139
95
|
def context_block(ctx)
|
|
140
96
|
return nil if ctx.blank?
|
|
141
97
|
lines = ["## Live Facility Snapshot"]
|
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
module YourAiInsight
|
|
2
2
|
class Configuration
|
|
3
|
-
# ──
|
|
4
|
-
# :claude (default) or :openrouter
|
|
5
|
-
attr_accessor :ai_provider
|
|
6
|
-
|
|
7
|
-
# ── Claude / Anthropic settings ───────────────────────────────────────────
|
|
8
|
-
attr_accessor :anthropic_api_key
|
|
9
|
-
attr_accessor :claude_model # default: claude-opus-4-5
|
|
10
|
-
attr_accessor :max_tokens # default: 2048
|
|
11
|
-
|
|
12
|
-
# ── OpenRouter settings ───────────────────────────────────────────────────
|
|
3
|
+
# ── OpenRouter (required) ──────────────────────────────────────────────────
|
|
13
4
|
attr_accessor :openrouter_api_key
|
|
14
|
-
#
|
|
15
|
-
attr_accessor :openrouter_model
|
|
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
|
|
16
7
|
attr_accessor :openrouter_site_url # optional — sent as HTTP-Referer
|
|
17
8
|
attr_accessor :openrouter_site_name # optional — sent as X-Title
|
|
18
9
|
|
|
19
|
-
# ──
|
|
10
|
+
# ── Response tuning ────────────────────────────────────────────────────────
|
|
11
|
+
attr_accessor :max_tokens # default: 2048
|
|
12
|
+
|
|
13
|
+
# ── Multi-tenancy: scope data to current user's locations/customers ────────
|
|
20
14
|
# Proc receives current_user and returns an Array of location_ids, or nil for all.
|
|
21
15
|
# Example: config.location_scope = ->(user) { user.locations.pluck(:id) }
|
|
22
16
|
attr_accessor :location_scope
|
|
@@ -96,16 +90,13 @@ module YourAiInsight
|
|
|
96
90
|
attr_accessor :sub_contractors_table # "sub_contractors"
|
|
97
91
|
|
|
98
92
|
def initialize
|
|
99
|
-
@ai_provider = :claude
|
|
100
|
-
@claude_model = "claude-opus-4-5"
|
|
101
|
-
@max_tokens = 2048
|
|
102
|
-
@company_name = "AllPro IFM"
|
|
103
|
-
|
|
104
93
|
@openrouter_model = "meta-llama/llama-3.3-70b-instruct:free"
|
|
105
94
|
@openrouter_site_url = nil
|
|
106
95
|
@openrouter_site_name = nil
|
|
107
|
-
@
|
|
108
|
-
@
|
|
96
|
+
@max_tokens = 2048
|
|
97
|
+
@company_name = "AllPro IFM"
|
|
98
|
+
@logo_url = nil
|
|
99
|
+
@default_recipients = []
|
|
109
100
|
|
|
110
101
|
# jobs
|
|
111
102
|
@jobs_table = "jobs"
|
|
@@ -136,11 +127,11 @@ module YourAiInsight
|
|
|
136
127
|
@bids_task_id_col = "task_id"
|
|
137
128
|
|
|
138
129
|
# expenses
|
|
139
|
-
@expenses_table
|
|
140
|
-
@expenses_budget_col
|
|
141
|
-
@expenses_actual_col
|
|
142
|
-
@expenses_job_id_col
|
|
143
|
-
@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"
|
|
144
135
|
|
|
145
136
|
# location_budgets
|
|
146
137
|
@location_budgets_table = "location_budgets"
|