ai-consultants 2.10.1
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.
- package/.claude/commands/ai-consultants:consult.md +94 -0
- package/.claude/commands/ai-consultants:debate.md +103 -0
- package/.claude/commands/ai-consultants:help.md +30 -0
- package/CLAUDE.md +839 -0
- package/LICENSE +21 -0
- package/README.md +758 -0
- package/SKILL.md +156 -0
- package/bin/ai-consultants +96 -0
- package/docs/COST_RATES.md +172 -0
- package/docs/JSON_SCHEMA.md +305 -0
- package/docs/SETUP.md +879 -0
- package/docs/SMART_ROUTING.md +219 -0
- package/docs/cost_rates.json +143 -0
- package/package.json +47 -0
- package/references/configuration.md +141 -0
- package/references/details.md +60 -0
- package/scripts/build_context.sh +562 -0
- package/scripts/classify_question.sh +148 -0
- package/scripts/config.sh +753 -0
- package/scripts/configure.sh +1093 -0
- package/scripts/consult_all.sh +934 -0
- package/scripts/debate_round.sh +421 -0
- package/scripts/doctor.sh +642 -0
- package/scripts/followup.sh +197 -0
- package/scripts/install.sh +319 -0
- package/scripts/lib/api.sh +640 -0
- package/scripts/lib/api_query.sh +228 -0
- package/scripts/lib/cache.sh +338 -0
- package/scripts/lib/chunking.sh +880 -0
- package/scripts/lib/code_optimizer.sh +671 -0
- package/scripts/lib/common.sh +1088 -0
- package/scripts/lib/costs.sh +674 -0
- package/scripts/lib/personas.sh +372 -0
- package/scripts/lib/progress.sh +251 -0
- package/scripts/lib/reflection.sh +447 -0
- package/scripts/lib/routing.sh +566 -0
- package/scripts/lib/schema.json +165 -0
- package/scripts/lib/session.sh +327 -0
- package/scripts/lib/symbol_map.sh +445 -0
- package/scripts/lib/voting.sh +495 -0
- package/scripts/peer_review.sh +460 -0
- package/scripts/preflight_check.sh +452 -0
- package/scripts/query_aider.sh +83 -0
- package/scripts/query_amp.sh +67 -0
- package/scripts/query_claude.sh +99 -0
- package/scripts/query_codex.sh +92 -0
- package/scripts/query_cursor.sh +72 -0
- package/scripts/query_deepseek.sh +17 -0
- package/scripts/query_gemini.sh +85 -0
- package/scripts/query_glm.sh +17 -0
- package/scripts/query_grok.sh +17 -0
- package/scripts/query_kilo.sh +140 -0
- package/scripts/query_kimi.sh +67 -0
- package/scripts/query_minimax.sh +17 -0
- package/scripts/query_mistral.sh +97 -0
- package/scripts/query_ollama.sh +328 -0
- package/scripts/query_qwen3.sh +93 -0
- package/scripts/setup_wizard.sh +367 -0
- package/scripts/skillport-install.sh +196 -0
- package/scripts/synthesize.sh +332 -0
- package/templates/consultation_report.md +323 -0
- package/templates/synthesis_prompt.md +120 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# query_mistral.sh - Query Mistral Vibe CLI or API
|
|
3
|
+
#
|
|
4
|
+
# Usage: ./query_mistral.sh "question" [context_file] [output_file]
|
|
5
|
+
#
|
|
6
|
+
# Environment variables:
|
|
7
|
+
# MISTRAL_MODEL - Model to use (default: mistral-large-3)
|
|
8
|
+
# MISTRAL_TIMEOUT - Timeout in seconds (default: 180)
|
|
9
|
+
# MISTRAL_USE_API - Use API mode instead of CLI (default: false)
|
|
10
|
+
# MISTRAL_API_KEY - API key for API mode
|
|
11
|
+
# ENABLE_PERSONA - Enable "The Devil's Advocate" persona (default: true)
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
17
|
+
source "$SCRIPT_DIR/lib/personas.sh"
|
|
18
|
+
|
|
19
|
+
# --- Parameters ---
|
|
20
|
+
QUERY="${1:-}"
|
|
21
|
+
CONTEXT_FILE="${2:-}"
|
|
22
|
+
OUTPUT_FILE="${3:-/tmp/mistral_response.json}"
|
|
23
|
+
|
|
24
|
+
# --- Configuration ---
|
|
25
|
+
ENABLE_PERSONA="${ENABLE_PERSONA:-true}"
|
|
26
|
+
CONSULTANT_NAME="Mistral"
|
|
27
|
+
|
|
28
|
+
# --- Build query ---
|
|
29
|
+
FULL_QUERY=$(build_full_query "$QUERY" "$CONTEXT_FILE")
|
|
30
|
+
validate_query "$FULL_QUERY" "Mistral Vibe" || exit 1
|
|
31
|
+
|
|
32
|
+
# --- Add persona if enabled ---
|
|
33
|
+
if [[ "$ENABLE_PERSONA" == "true" ]]; then
|
|
34
|
+
FULL_QUERY=$(build_query_with_persona "$CONSULTANT_NAME" "$FULL_QUERY")
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# --- Timestamp for metadata ---
|
|
38
|
+
START_TIME=$(get_timestamp_ms)
|
|
39
|
+
|
|
40
|
+
# --- Execution (CLI or API mode) ---
|
|
41
|
+
TEMP_OUTPUT=$(mktemp)
|
|
42
|
+
|
|
43
|
+
if is_api_mode "mistral"; then
|
|
44
|
+
# --- API Mode ---
|
|
45
|
+
log_api_mode_status "mistral"
|
|
46
|
+
validate_api_mode "mistral" || exit 1
|
|
47
|
+
|
|
48
|
+
source "$SCRIPT_DIR/lib/api_query.sh"
|
|
49
|
+
|
|
50
|
+
run_api_mode_query \
|
|
51
|
+
"$CONSULTANT_NAME" \
|
|
52
|
+
"$MISTRAL_MODEL" \
|
|
53
|
+
"$FULL_QUERY" \
|
|
54
|
+
"$TEMP_OUTPUT" \
|
|
55
|
+
"$MISTRAL_TIMEOUT_SECONDS"
|
|
56
|
+
|
|
57
|
+
exit_code=$?
|
|
58
|
+
else
|
|
59
|
+
# --- CLI Mode ---
|
|
60
|
+
log_api_mode_status "mistral"
|
|
61
|
+
check_command "$MISTRAL_CMD" "Mistral Vibe CLI" "pip install mistral-vibe" || exit 1
|
|
62
|
+
|
|
63
|
+
run_query \
|
|
64
|
+
"Mistral Vibe" \
|
|
65
|
+
"$TEMP_OUTPUT" \
|
|
66
|
+
"$MISTRAL_TIMEOUT_SECONDS" \
|
|
67
|
+
"$MISTRAL_CMD" -p "$FULL_QUERY" --output text < /dev/null
|
|
68
|
+
|
|
69
|
+
exit_code=$?
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# --- Calculate latency ---
|
|
73
|
+
END_TIME=$(get_timestamp_ms)
|
|
74
|
+
LATENCY_MS=$((END_TIME - START_TIME))
|
|
75
|
+
|
|
76
|
+
# --- Configuration for response building ---
|
|
77
|
+
MODEL_USED="${MISTRAL_MODEL:-mistral-large-3}"
|
|
78
|
+
PERSONA_NAME=$(get_persona_name "$CONSULTANT_NAME")
|
|
79
|
+
|
|
80
|
+
# --- Post-processing: wrap in full schema using shared helpers ---
|
|
81
|
+
if [[ $exit_code -eq 0 && -f "$TEMP_OUTPUT" && -s "$TEMP_OUTPUT" ]]; then
|
|
82
|
+
RAW_RESPONSE=$(cat "$TEMP_OUTPUT")
|
|
83
|
+
rm -f "$TEMP_OUTPUT"
|
|
84
|
+
|
|
85
|
+
# Try to parse as structured JSON
|
|
86
|
+
if echo "$RAW_RESPONSE" | jq -e '.response.summary' > /dev/null 2>&1; then
|
|
87
|
+
build_structured_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" "$RAW_RESPONSE" "$LATENCY_MS" > "$OUTPUT_FILE"
|
|
88
|
+
else
|
|
89
|
+
build_fallback_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" "$RAW_RESPONSE" "$LATENCY_MS" > "$OUTPUT_FILE"
|
|
90
|
+
fi
|
|
91
|
+
else
|
|
92
|
+
rm -f "$TEMP_OUTPUT"
|
|
93
|
+
build_error_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" "Query failed with exit code $exit_code" "$LATENCY_MS" > "$OUTPUT_FILE"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
cat "$OUTPUT_FILE"
|
|
97
|
+
exit $exit_code
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# query_ollama.sh - Query local Ollama models (v2.2 with Persona and Confidence)
|
|
3
|
+
#
|
|
4
|
+
# Enables fully local, privacy-preserving AI consultation using Ollama.
|
|
5
|
+
# Zero API costs, no data leaves your machine.
|
|
6
|
+
#
|
|
7
|
+
# Usage: ./query_ollama.sh "question" [context_file] [output_file]
|
|
8
|
+
#
|
|
9
|
+
# Environment variables:
|
|
10
|
+
# OLLAMA_MODEL - Model to use (default: llama3.2)
|
|
11
|
+
# OLLAMA_HOST - Ollama server URL (default: http://localhost:11434)
|
|
12
|
+
# OLLAMA_TIMEOUT - Timeout in seconds (default: 300)
|
|
13
|
+
# ENABLE_PERSONA - Enable "The Local Expert" persona (default: true)
|
|
14
|
+
#
|
|
15
|
+
# Prerequisites:
|
|
16
|
+
# - Ollama installed: curl -fsSL https://ollama.com/install.sh | sh
|
|
17
|
+
# - Model pulled: ollama pull llama3.2
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
# Cleanup trap for temp files
|
|
22
|
+
TEMP_OUTPUT=""
|
|
23
|
+
cleanup() {
|
|
24
|
+
[[ -n "$TEMP_OUTPUT" && -f "$TEMP_OUTPUT" ]] && rm -f "$TEMP_OUTPUT"
|
|
25
|
+
}
|
|
26
|
+
trap cleanup EXIT
|
|
27
|
+
|
|
28
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
29
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
30
|
+
source "$SCRIPT_DIR/lib/personas.sh"
|
|
31
|
+
|
|
32
|
+
# =============================================================================
|
|
33
|
+
# CONFIGURATION
|
|
34
|
+
# =============================================================================
|
|
35
|
+
|
|
36
|
+
OLLAMA_MODEL="${OLLAMA_MODEL:-llama3.2}"
|
|
37
|
+
OLLAMA_HOST="${OLLAMA_HOST:-http://localhost:11434}"
|
|
38
|
+
OLLAMA_TIMEOUT="${OLLAMA_TIMEOUT:-300}"
|
|
39
|
+
OLLAMA_TEMPERATURE="${OLLAMA_TEMPERATURE:-0.7}"
|
|
40
|
+
ENABLE_PERSONA="${ENABLE_PERSONA:-true}"
|
|
41
|
+
CONSULTANT_NAME="Ollama"
|
|
42
|
+
|
|
43
|
+
# Parameters
|
|
44
|
+
QUERY="${1:-}"
|
|
45
|
+
CONTEXT_FILE="${2:-}"
|
|
46
|
+
OUTPUT_FILE="${3:-/tmp/ollama_response.json}"
|
|
47
|
+
|
|
48
|
+
# =============================================================================
|
|
49
|
+
# PERSONA DEFINITION FOR OLLAMA
|
|
50
|
+
# =============================================================================
|
|
51
|
+
|
|
52
|
+
# Register Ollama persona if not already defined
|
|
53
|
+
OLLAMA_PERSONA_ID="${OLLAMA_PERSONA_ID:-16}"
|
|
54
|
+
OLLAMA_PERSONA="${OLLAMA_PERSONA:-The Local Expert}"
|
|
55
|
+
OLLAMA_PERSONA_PROMPT="${OLLAMA_PERSONA_PROMPT:-You are 'The Local Expert' - a privacy-conscious AI assistant running locally. You prioritize:
|
|
56
|
+
- Practical, implementable solutions
|
|
57
|
+
- Clear explanations without assumptions about external services
|
|
58
|
+
- Security and privacy considerations
|
|
59
|
+
- Efficient use of local resources}"
|
|
60
|
+
|
|
61
|
+
# =============================================================================
|
|
62
|
+
# PREREQUISITE CHECKS
|
|
63
|
+
# =============================================================================
|
|
64
|
+
|
|
65
|
+
# Check if Ollama CLI is available
|
|
66
|
+
if ! command -v ollama &> /dev/null; then
|
|
67
|
+
log_error "Ollama CLI not found"
|
|
68
|
+
log_info "Install with: curl -fsSL https://ollama.com/install.sh | sh"
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Check if Ollama server is running
|
|
73
|
+
check_ollama_server() {
|
|
74
|
+
if ! curl -s "$OLLAMA_HOST/api/tags" > /dev/null 2>&1; then
|
|
75
|
+
log_error "Ollama server not responding at $OLLAMA_HOST"
|
|
76
|
+
log_info "Start with: ollama serve"
|
|
77
|
+
return 1
|
|
78
|
+
fi
|
|
79
|
+
return 0
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# Check if the requested model is available
|
|
83
|
+
check_model_available() {
|
|
84
|
+
local model="$1"
|
|
85
|
+
local available_models
|
|
86
|
+
available_models=$(curl -s "$OLLAMA_HOST/api/tags" 2>/dev/null | jq -r '.models[].name // empty' 2>/dev/null)
|
|
87
|
+
|
|
88
|
+
if [[ -z "$available_models" ]]; then
|
|
89
|
+
log_warn "Could not verify model availability"
|
|
90
|
+
return 0
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Check if model (or base model) is available
|
|
94
|
+
local base_model="${model%%:*}"
|
|
95
|
+
if echo "$available_models" | grep -q "^${base_model}"; then
|
|
96
|
+
return 0
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
log_error "Model '$model' not found locally"
|
|
100
|
+
log_info "Pull with: ollama pull $model"
|
|
101
|
+
return 1
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Run checks
|
|
105
|
+
check_ollama_server || exit 1
|
|
106
|
+
check_model_available "$OLLAMA_MODEL" || exit 1
|
|
107
|
+
|
|
108
|
+
# =============================================================================
|
|
109
|
+
# BUILD QUERY
|
|
110
|
+
# =============================================================================
|
|
111
|
+
|
|
112
|
+
# --- Build query ---
|
|
113
|
+
FULL_QUERY=$(build_full_query "$QUERY" "$CONTEXT_FILE")
|
|
114
|
+
validate_query "$FULL_QUERY" "Ollama" || exit 1
|
|
115
|
+
|
|
116
|
+
# --- Add persona if enabled ---
|
|
117
|
+
if [[ "$ENABLE_PERSONA" == "true" ]]; then
|
|
118
|
+
PERSONA_PROMPT="$OLLAMA_PERSONA_PROMPT
|
|
119
|
+
|
|
120
|
+
You must respond in this EXACT JSON format:
|
|
121
|
+
{
|
|
122
|
+
\"response\": {
|
|
123
|
+
\"summary\": \"Brief TL;DR (2-3 sentences)\",
|
|
124
|
+
\"detailed\": \"Full detailed response\",
|
|
125
|
+
\"approach\": \"Name of the approach/solution\",
|
|
126
|
+
\"pros\": [\"advantage 1\", \"advantage 2\"],
|
|
127
|
+
\"cons\": [\"disadvantage 1\", \"disadvantage 2\"],
|
|
128
|
+
\"caveats\": [\"important consideration\"]
|
|
129
|
+
},
|
|
130
|
+
\"confidence\": {
|
|
131
|
+
\"score\": 7,
|
|
132
|
+
\"reasoning\": \"Why this confidence level\",
|
|
133
|
+
\"uncertainty_factors\": [\"What could affect this answer\"]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
IMPORTANT: Respond ONLY with valid JSON, no markdown or additional text.
|
|
138
|
+
|
|
139
|
+
"
|
|
140
|
+
FULL_QUERY="${PERSONA_PROMPT}${FULL_QUERY}"
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# =============================================================================
|
|
144
|
+
# EXECUTE QUERY
|
|
145
|
+
# =============================================================================
|
|
146
|
+
|
|
147
|
+
# --- Timestamp for metadata ---
|
|
148
|
+
START_TIME=$(get_timestamp_ms)
|
|
149
|
+
|
|
150
|
+
# --- Build API request body ---
|
|
151
|
+
REQUEST_BODY=$(jq -n \
|
|
152
|
+
--arg model "$OLLAMA_MODEL" \
|
|
153
|
+
--arg prompt "$FULL_QUERY" \
|
|
154
|
+
--argjson temperature "$OLLAMA_TEMPERATURE" \
|
|
155
|
+
'{
|
|
156
|
+
model: $model,
|
|
157
|
+
prompt: $prompt,
|
|
158
|
+
stream: false,
|
|
159
|
+
options: {
|
|
160
|
+
temperature: $temperature
|
|
161
|
+
}
|
|
162
|
+
}')
|
|
163
|
+
|
|
164
|
+
# --- Execute query ---
|
|
165
|
+
log_info "Consulting Ollama ($OLLAMA_MODEL) locally..."
|
|
166
|
+
|
|
167
|
+
TEMP_OUTPUT=$(mktemp)
|
|
168
|
+
HTTP_CODE=$(curl -s -w "%{http_code}" \
|
|
169
|
+
--max-time "$OLLAMA_TIMEOUT" \
|
|
170
|
+
-X POST "$OLLAMA_HOST/api/generate" \
|
|
171
|
+
-H "Content-Type: application/json" \
|
|
172
|
+
-d "$REQUEST_BODY" \
|
|
173
|
+
-o "$TEMP_OUTPUT" 2>/dev/null) || HTTP_CODE="000"
|
|
174
|
+
|
|
175
|
+
exit_code=$?
|
|
176
|
+
|
|
177
|
+
# --- Calculate latency ---
|
|
178
|
+
END_TIME=$(get_timestamp_ms)
|
|
179
|
+
LATENCY_MS=$((END_TIME - START_TIME))
|
|
180
|
+
|
|
181
|
+
# =============================================================================
|
|
182
|
+
# PROCESS RESPONSE
|
|
183
|
+
# =============================================================================
|
|
184
|
+
|
|
185
|
+
if [[ "$HTTP_CODE" == "200" && -f "$TEMP_OUTPUT" && -s "$TEMP_OUTPUT" ]]; then
|
|
186
|
+
# Extract response from Ollama format
|
|
187
|
+
RAW_RESPONSE=$(cat "$TEMP_OUTPUT")
|
|
188
|
+
|
|
189
|
+
# Get the response text and metadata
|
|
190
|
+
INNER_RESPONSE=$(echo "$RAW_RESPONSE" | jq -r '.response // empty' 2>/dev/null)
|
|
191
|
+
TOTAL_DURATION=$(echo "$RAW_RESPONSE" | jq -r '.total_duration // 0' 2>/dev/null)
|
|
192
|
+
EVAL_COUNT=$(echo "$RAW_RESPONSE" | jq -r '.eval_count // 0' 2>/dev/null)
|
|
193
|
+
|
|
194
|
+
if [[ -z "$INNER_RESPONSE" ]]; then
|
|
195
|
+
log_error "Empty response from Ollama"
|
|
196
|
+
exit_code=1
|
|
197
|
+
else
|
|
198
|
+
# Try to parse as JSON (from our structured prompt)
|
|
199
|
+
PARSED_JSON=""
|
|
200
|
+
|
|
201
|
+
# First try: direct JSON parse
|
|
202
|
+
if echo "$INNER_RESPONSE" | jq -e '.response.summary' > /dev/null 2>&1; then
|
|
203
|
+
PARSED_JSON="$INNER_RESPONSE"
|
|
204
|
+
else
|
|
205
|
+
# Second try: extract JSON from text
|
|
206
|
+
EXTRACTED=$(echo "$INNER_RESPONSE" | sed -n '/{/,/}/p' | tr '\n' ' ')
|
|
207
|
+
if [[ -n "$EXTRACTED" ]] && echo "$EXTRACTED" | jq -e '.response.summary' > /dev/null 2>&1; then
|
|
208
|
+
PARSED_JSON="$EXTRACTED"
|
|
209
|
+
fi
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
if [[ -n "$PARSED_JSON" ]]; then
|
|
213
|
+
# Structured response - wrap with metadata
|
|
214
|
+
jq -n \
|
|
215
|
+
--arg consultant "$CONSULTANT_NAME" \
|
|
216
|
+
--arg model "$OLLAMA_MODEL" \
|
|
217
|
+
--arg persona "$OLLAMA_PERSONA" \
|
|
218
|
+
--argjson inner "$PARSED_JSON" \
|
|
219
|
+
--argjson eval_count "$EVAL_COUNT" \
|
|
220
|
+
--argjson latency "$LATENCY_MS" \
|
|
221
|
+
--arg timestamp "$(date -Iseconds)" \
|
|
222
|
+
'{
|
|
223
|
+
consultant: $consultant,
|
|
224
|
+
model: $model,
|
|
225
|
+
persona: $persona,
|
|
226
|
+
response: $inner.response,
|
|
227
|
+
confidence: $inner.confidence,
|
|
228
|
+
metadata: {
|
|
229
|
+
tokens_used: $eval_count,
|
|
230
|
+
latency_ms: $latency,
|
|
231
|
+
model_version: $model,
|
|
232
|
+
timestamp: $timestamp,
|
|
233
|
+
local: true,
|
|
234
|
+
cost: 0
|
|
235
|
+
}
|
|
236
|
+
}' > "$OUTPUT_FILE"
|
|
237
|
+
else
|
|
238
|
+
# Unstructured response - create base structure
|
|
239
|
+
jq -n \
|
|
240
|
+
--arg consultant "$CONSULTANT_NAME" \
|
|
241
|
+
--arg model "$OLLAMA_MODEL" \
|
|
242
|
+
--arg persona "$OLLAMA_PERSONA" \
|
|
243
|
+
--arg response "$INNER_RESPONSE" \
|
|
244
|
+
--argjson eval_count "$EVAL_COUNT" \
|
|
245
|
+
--argjson latency "$LATENCY_MS" \
|
|
246
|
+
--arg timestamp "$(date -Iseconds)" \
|
|
247
|
+
'{
|
|
248
|
+
consultant: $consultant,
|
|
249
|
+
model: $model,
|
|
250
|
+
persona: $persona,
|
|
251
|
+
response: {
|
|
252
|
+
summary: "See detailed response below",
|
|
253
|
+
detailed: $response,
|
|
254
|
+
approach: "local_inference",
|
|
255
|
+
pros: ["No API costs", "Full privacy", "No rate limits"],
|
|
256
|
+
cons: ["Limited by local hardware"],
|
|
257
|
+
caveats: ["Response format not structured"]
|
|
258
|
+
},
|
|
259
|
+
confidence: {
|
|
260
|
+
score: 6,
|
|
261
|
+
reasoning: "Local model response - confidence estimated",
|
|
262
|
+
uncertainty_factors: ["Unstructured output format"]
|
|
263
|
+
},
|
|
264
|
+
metadata: {
|
|
265
|
+
tokens_used: $eval_count,
|
|
266
|
+
latency_ms: $latency,
|
|
267
|
+
model_version: $model,
|
|
268
|
+
timestamp: $timestamp,
|
|
269
|
+
local: true,
|
|
270
|
+
cost: 0
|
|
271
|
+
}
|
|
272
|
+
}' > "$OUTPUT_FILE"
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
log_success "[Ollama] Response received (${EVAL_COUNT} tokens, ${LATENCY_MS}ms)"
|
|
276
|
+
rm -f "$TEMP_OUTPUT"
|
|
277
|
+
cat "$OUTPUT_FILE"
|
|
278
|
+
exit 0
|
|
279
|
+
fi
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
# --- Error handling ---
|
|
283
|
+
ERROR_MSG="Query failed"
|
|
284
|
+
if [[ "$HTTP_CODE" == "000" ]]; then
|
|
285
|
+
ERROR_MSG="Connection failed - is Ollama server running?"
|
|
286
|
+
elif [[ "$HTTP_CODE" != "200" ]]; then
|
|
287
|
+
ERROR_MSG="HTTP error $HTTP_CODE"
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
log_error "[Ollama] $ERROR_MSG"
|
|
291
|
+
|
|
292
|
+
jq -n \
|
|
293
|
+
--arg consultant "$CONSULTANT_NAME" \
|
|
294
|
+
--arg model "$OLLAMA_MODEL" \
|
|
295
|
+
--arg persona "$OLLAMA_PERSONA" \
|
|
296
|
+
--argjson latency "$LATENCY_MS" \
|
|
297
|
+
--arg timestamp "$(date -Iseconds)" \
|
|
298
|
+
--arg error "$ERROR_MSG" \
|
|
299
|
+
'{
|
|
300
|
+
consultant: $consultant,
|
|
301
|
+
model: $model,
|
|
302
|
+
persona: $persona,
|
|
303
|
+
response: {
|
|
304
|
+
summary: "ERROR: Consultation failed",
|
|
305
|
+
detailed: $error,
|
|
306
|
+
approach: "error",
|
|
307
|
+
pros: [],
|
|
308
|
+
cons: [],
|
|
309
|
+
caveats: []
|
|
310
|
+
},
|
|
311
|
+
confidence: {
|
|
312
|
+
score: 0,
|
|
313
|
+
reasoning: "Consultation failed",
|
|
314
|
+
uncertainty_factors: ["Execution error"]
|
|
315
|
+
},
|
|
316
|
+
metadata: {
|
|
317
|
+
latency_ms: $latency,
|
|
318
|
+
model_version: $model,
|
|
319
|
+
timestamp: $timestamp,
|
|
320
|
+
local: true,
|
|
321
|
+
cost: 0,
|
|
322
|
+
error: $error
|
|
323
|
+
}
|
|
324
|
+
}' > "$OUTPUT_FILE"
|
|
325
|
+
|
|
326
|
+
rm -f "$TEMP_OUTPUT"
|
|
327
|
+
cat "$OUTPUT_FILE"
|
|
328
|
+
exit 1
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# query_qwen3.sh - Query Qwen3 via CLI (qwen-code) or HTTP API
|
|
3
|
+
#
|
|
4
|
+
# Usage: ./query_qwen3.sh "question" [context_file] [output_file]
|
|
5
|
+
#
|
|
6
|
+
# Environment variables:
|
|
7
|
+
# QWEN3_MODEL - Model to use (default: qwen3.5-plus)
|
|
8
|
+
# QWEN3_TIMEOUT - Timeout in seconds (default: 180)
|
|
9
|
+
# QWEN3_USE_API - Use API mode instead of CLI (default: false)
|
|
10
|
+
# QWEN3_API_KEY - API key for DashScope API (required for API mode)
|
|
11
|
+
# ENABLE_PERSONA - Enable "The Analyst" persona (default: true)
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
source "$SCRIPT_DIR/lib/common.sh"
|
|
17
|
+
source "$SCRIPT_DIR/lib/personas.sh"
|
|
18
|
+
|
|
19
|
+
# --- Parameters ---
|
|
20
|
+
QUERY="${1:-}"
|
|
21
|
+
CONTEXT_FILE="${2:-}"
|
|
22
|
+
OUTPUT_FILE="${3:-/tmp/qwen3_response.json}"
|
|
23
|
+
|
|
24
|
+
# --- Configuration ---
|
|
25
|
+
ENABLE_PERSONA="${ENABLE_PERSONA:-true}"
|
|
26
|
+
CONSULTANT_NAME="Qwen3"
|
|
27
|
+
|
|
28
|
+
# --- Build query ---
|
|
29
|
+
FULL_QUERY=$(build_full_query "$QUERY" "$CONTEXT_FILE")
|
|
30
|
+
validate_query "$FULL_QUERY" "Qwen3" || exit 1
|
|
31
|
+
|
|
32
|
+
# --- Add persona if enabled ---
|
|
33
|
+
if [[ "$ENABLE_PERSONA" == "true" ]]; then
|
|
34
|
+
FULL_QUERY=$(build_query_with_persona "$CONSULTANT_NAME" "$FULL_QUERY")
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# --- Timestamp for metadata ---
|
|
38
|
+
START_TIME=$(get_timestamp_ms)
|
|
39
|
+
|
|
40
|
+
# --- Execution (CLI or API mode) ---
|
|
41
|
+
TEMP_OUTPUT=$(mktemp)
|
|
42
|
+
|
|
43
|
+
if is_api_mode "qwen3"; then
|
|
44
|
+
# --- API Mode (DashScope) ---
|
|
45
|
+
log_api_mode_status "qwen3"
|
|
46
|
+
validate_api_mode "qwen3" || exit 1
|
|
47
|
+
|
|
48
|
+
source "$SCRIPT_DIR/lib/api_query.sh"
|
|
49
|
+
|
|
50
|
+
run_api_mode_query \
|
|
51
|
+
"$CONSULTANT_NAME" \
|
|
52
|
+
"$QWEN3_MODEL" \
|
|
53
|
+
"$FULL_QUERY" \
|
|
54
|
+
"$TEMP_OUTPUT" \
|
|
55
|
+
"$QWEN3_TIMEOUT_SECONDS"
|
|
56
|
+
|
|
57
|
+
exit_code=$?
|
|
58
|
+
else
|
|
59
|
+
# --- CLI Mode (qwen-code) ---
|
|
60
|
+
log_api_mode_status "qwen3"
|
|
61
|
+
check_command "$QWEN3_CMD" "Qwen CLI" "npm install -g @qwen-code/qwen-code@latest" || exit 1
|
|
62
|
+
|
|
63
|
+
# qwen-code uses -p for prompt, reads from stdin with -p -
|
|
64
|
+
# Build command args with optional model
|
|
65
|
+
QWEN_ARGS=("$QWEN3_CMD")
|
|
66
|
+
if [[ -n "${QWEN3_MODEL:-}" ]]; then
|
|
67
|
+
QWEN_ARGS+=("-m" "$QWEN3_MODEL")
|
|
68
|
+
fi
|
|
69
|
+
QWEN_ARGS+=("-p" "-")
|
|
70
|
+
|
|
71
|
+
echo "$FULL_QUERY" | run_query \
|
|
72
|
+
"Qwen3" \
|
|
73
|
+
"$TEMP_OUTPUT" \
|
|
74
|
+
"$QWEN3_TIMEOUT_SECONDS" \
|
|
75
|
+
"${QWEN_ARGS[@]}"
|
|
76
|
+
|
|
77
|
+
exit_code=$?
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# --- Calculate latency ---
|
|
81
|
+
END_TIME=$(get_timestamp_ms)
|
|
82
|
+
LATENCY_MS=$((END_TIME - START_TIME))
|
|
83
|
+
|
|
84
|
+
# --- Configuration for response building ---
|
|
85
|
+
MODEL_USED="${QWEN3_MODEL:-qwen3.5-plus}"
|
|
86
|
+
PERSONA_NAME=$(get_persona_name "$CONSULTANT_NAME")
|
|
87
|
+
|
|
88
|
+
# --- Post-processing: use shared helper ---
|
|
89
|
+
process_consultant_response "$CONSULTANT_NAME" "$MODEL_USED" "$PERSONA_NAME" \
|
|
90
|
+
"$TEMP_OUTPUT" "$OUTPUT_FILE" "$exit_code" "$LATENCY_MS"
|
|
91
|
+
|
|
92
|
+
cat "$OUTPUT_FILE"
|
|
93
|
+
exit $exit_code
|