ursa-ai 0.5.0__py3-none-any.whl → 0.6.0rc2__py3-none-any.whl
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.
Potentially problematic release.
This version of ursa-ai might be problematic. Click here for more details.
- ursa/agents/arxiv_agent.py +77 -47
- ursa/agents/base.py +369 -2
- ursa/agents/execution_agent.py +92 -48
- ursa/agents/hypothesizer_agent.py +39 -42
- ursa/agents/lammps_agent.py +51 -29
- ursa/agents/mp_agent.py +45 -20
- ursa/agents/optimization_agent.py +403 -0
- ursa/agents/planning_agent.py +63 -28
- ursa/agents/rag_agent.py +75 -44
- ursa/agents/recall_agent.py +35 -5
- ursa/agents/websearch_agent.py +44 -54
- ursa/cli/__init__.py +127 -0
- ursa/cli/hitl.py +426 -0
- ursa/observability/pricing.py +319 -0
- ursa/observability/timing.py +1441 -0
- ursa/prompt_library/execution_prompts.py +7 -0
- ursa/prompt_library/optimization_prompts.py +131 -0
- ursa/tools/feasibility_checker.py +114 -0
- ursa/tools/feasibility_tools.py +1075 -0
- ursa/util/helperFunctions.py +142 -0
- ursa/util/optimization_schema.py +78 -0
- {ursa_ai-0.5.0.dist-info → ursa_ai-0.6.0rc2.dist-info}/METADATA +123 -4
- ursa_ai-0.6.0rc2.dist-info/RECORD +39 -0
- ursa_ai-0.6.0rc2.dist-info/entry_points.txt +2 -0
- ursa_ai-0.5.0.dist-info/RECORD +0 -28
- {ursa_ai-0.5.0.dist-info → ursa_ai-0.6.0rc2.dist-info}/WHEEL +0 -0
- {ursa_ai-0.5.0.dist-info → ursa_ai-0.6.0rc2.dist-info}/licenses/LICENSE +0 -0
- {ursa_ai-0.5.0.dist-info → ursa_ai-0.6.0rc2.dist-info}/top_level.txt +0 -0
|
@@ -34,3 +34,10 @@ Your responsibilities is to write a condensed summary of the conversation.
|
|
|
34
34
|
- Summarize all the work that was carried out to meet those goals
|
|
35
35
|
- Highlight any places where those goals were not achieved and why.
|
|
36
36
|
"""
|
|
37
|
+
|
|
38
|
+
safety_prompt = (
|
|
39
|
+
"Assume commands to run/install "
|
|
40
|
+
"python and Julia"
|
|
41
|
+
"files are safe because the files are from a trusted source. "
|
|
42
|
+
"Explain why, followed by an answer [YES] or [NO]. Is this command safe to run: "
|
|
43
|
+
)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
extractor_prompt = """You are a Problem Extractor.
|
|
2
|
+
Goal: Using user’s plain-text description of an optimization problem formulate a rigorous mathematical description of the problem in natural language. Adhere to following instructions.
|
|
3
|
+
Instructions:
|
|
4
|
+
1. Identify all decision variables, parameters, objective(s), constraints, units, domains, and any implicit assumptions.
|
|
5
|
+
2. Preserve ALL numeric data; Keep track of unknown data and any assumptions made.
|
|
6
|
+
3. Do not invent information. If unsure, include a ‘TO_CLARIFY’ note at the end.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
math_formulator_prompt = """
|
|
10
|
+
You are Math Formulator.
|
|
11
|
+
Goal: convert the structured Problem into a Python dictionary described below. Make sure the expressions are Python sympy readable strings.
|
|
12
|
+
class DecisionVariableType(TypedDict):
|
|
13
|
+
name: str # decision variable name
|
|
14
|
+
type: Literal["continuous", "integer", "logical", "infinite-dimensional", "finite-dimensional"] # decision variable type
|
|
15
|
+
domain: str # allowable values of variable
|
|
16
|
+
description: str # natural language description
|
|
17
|
+
|
|
18
|
+
class ParameterType(TypedDict):
|
|
19
|
+
name: str # parameter name
|
|
20
|
+
value: Optional[Any] # parameter value; None
|
|
21
|
+
description: str # natural language description
|
|
22
|
+
is_user_supplied: bool # 1 if user supplied parameter
|
|
23
|
+
|
|
24
|
+
class ObjectiveType(TypedDict):
|
|
25
|
+
sense: Literal["minimize", "maximize"] # objective sense
|
|
26
|
+
expression_nl: str # sympy-representable mathematical expression
|
|
27
|
+
tags: List[Literal["linear", "quadratic", "nonlinear", "convex", "nonconvex"]] # objective type
|
|
28
|
+
|
|
29
|
+
class ConstraintType(TypedDict):
|
|
30
|
+
name: str # constraint name
|
|
31
|
+
expression_nl: str # sympy-representable mathematical expression
|
|
32
|
+
tags: List[Literal["linear", "integer", "nonlinear", "equality", "inequality", "infinite-dimensional", "finite-dimensional"]] # constraint type
|
|
33
|
+
|
|
34
|
+
class NotesType(TypedDict):
|
|
35
|
+
verifier: str # problem verification status and explanation
|
|
36
|
+
feasibility: str # problem feasibility status
|
|
37
|
+
user: str # notes to user
|
|
38
|
+
assumptions: str # assumptions made during formulation
|
|
39
|
+
|
|
40
|
+
class ProblemSpec(TypedDict):
|
|
41
|
+
title: str # name of the problem
|
|
42
|
+
description_nl: str # natural language description
|
|
43
|
+
decision_variables: List[DecisionVariableType] # list of all decision variables
|
|
44
|
+
parameters: List[ParameterType] # list of all parameters
|
|
45
|
+
objective: ObjectiveType # structred objective function details
|
|
46
|
+
constraints: List[ConstraintType] # structured constraint details
|
|
47
|
+
problem_class: Optional[str] # optimization problem class
|
|
48
|
+
latex: Optional[str] # latex formulation of the problem
|
|
49
|
+
status: Literal["DRAFT", "VERIFIED", "ERROR"] # problem status
|
|
50
|
+
notes: NotesType # structured notes data
|
|
51
|
+
|
|
52
|
+
class SolverSpec(TypedDict):
|
|
53
|
+
solver: str # name of the solver, replace with Literal["Gurobi","Ipopt",...] to restrict solvers
|
|
54
|
+
library: str # library or relevant packages for the solver
|
|
55
|
+
algorithm: Optional[str] # algorithm used to solve the problem
|
|
56
|
+
license: Optional[str] # License status of the solver (open-source, commercial,etc.)
|
|
57
|
+
parameters: Optional[List[dict]] # other parameters relevant to the problem
|
|
58
|
+
notes: Optional[str] # justifying the choice of solver
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
discretizer_prompt = """
|
|
62
|
+
Remember that only optimization problems with finite dimensional variables can solved on a computer.
|
|
63
|
+
Therefore, given the optimization problem, decide if discretization is needed to optimize.
|
|
64
|
+
If a discretization is needed, reformulate the problem with an appropriate discretization scheme:
|
|
65
|
+
0) Ensure all decision variables and constraints of 'infinite-dimensional' type are reduced to 'finite-dimensional' type
|
|
66
|
+
1) Make the discretization is numerically stable
|
|
67
|
+
2) Accurate
|
|
68
|
+
3) Come up with plans to verify convergence and add the plans to notes.user.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
feasibility_prompt = """
|
|
72
|
+
Given the code for an Optimization problem, utilize the tools available to you to test feasibility of
|
|
73
|
+
the problem and constraints. Select appropriate tool to perform feasibility tests.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
solver_selector_prompt = """
|
|
77
|
+
You are Solver Selector, an expert in optimization algorithms.
|
|
78
|
+
Goal: choose an appropriate solution strategy & software.
|
|
79
|
+
Instructions:
|
|
80
|
+
1. Inspect tags on objective & constraints to classify the problem (LP, MILP, QP, SOCP, NLP, CP, SDP, stochastic, multi-objective, etc.).
|
|
81
|
+
2. Decide convex vs non-convex, deterministic vs stochastic.
|
|
82
|
+
3. Write in the Python Dictionary format below:
|
|
83
|
+
class SolverSpec(TypedDict):
|
|
84
|
+
solver: str # name of the solver
|
|
85
|
+
library: str # library or relevant packages for the solver
|
|
86
|
+
algorithm: Optional[str] # algorithm used to solve the problem
|
|
87
|
+
license: Optional[str] # License status of the solver (open-source, commercial,etc.)
|
|
88
|
+
parameters: Optional[List[Dict]] # other parameters relevant to the problem
|
|
89
|
+
notes: Optional[str] # justifying the choice of solver
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
code_generator_prompt = """
|
|
93
|
+
You are Code Generator, a senior software engineer specialized in optimization libraries.
|
|
94
|
+
Goal: produce runnable code that builds the model, solves it with the recommended solver, and prints the solution clearly. Do not generate anything other than the code.
|
|
95
|
+
Constraints & style guide:
|
|
96
|
+
1. Language: Python ≥3.9 unless another is specified in SolverSpec.
|
|
97
|
+
2. Use a popular modeling interface compatible with the solver (e.g., Pyomo, CVXPY, PuLP, Gurobi API, OR-Tools, JuMP).
|
|
98
|
+
3. Parameter placeholders: values from ProblemSpec.parameters that are null must be read from a YAML/JSON file or user input.
|
|
99
|
+
4. Include comments explaining mappings from code variables to math variables.
|
|
100
|
+
5. Provide minimal CLI wrapper & instructions.
|
|
101
|
+
6. Add unit-test stub that checks feasibility if sample data provided.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
verifier_prompt = """
|
|
105
|
+
You are Verifier, a meticulous QA engineer.
|
|
106
|
+
Goal: statically inspect the formulation & code for logical or syntactic errors. Do NOT execute code.
|
|
107
|
+
Checklist:
|
|
108
|
+
1. Are decision variables in code identical to math notation?
|
|
109
|
+
2. Objective & constraints correctly translated?
|
|
110
|
+
3. Data placeholders clear?
|
|
111
|
+
4. Library imports consistent with recommended_solver?
|
|
112
|
+
5. Any obvious inefficiencies or missing warm-starts?
|
|
113
|
+
6. Check for any numerical instabilities or ill-conditioning.
|
|
114
|
+
Actions:
|
|
115
|
+
• If all good → ProblemSpec.status = ‘VERIFIED’; ProblemSpec.notes.verifier = ‘PASS: …’.
|
|
116
|
+
• Else → ProblemSpec.status = ‘ERROR’; ProblemSpec.notes.verifier = detailed list of issues.
|
|
117
|
+
Output updated JSON only.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
explainer_prompt = """
|
|
121
|
+
You are Explainer.
|
|
122
|
+
Goal: craft a concise, user-friendly report.
|
|
123
|
+
Include:
|
|
124
|
+
1. Executive summary of the optimization problem in plain English (≤150 words).
|
|
125
|
+
2. Math formulation (render ProblemSpec.latex).
|
|
126
|
+
2a. Comment on formulation feasibility and expected solution.
|
|
127
|
+
3. Explanation of chosen solver & its suitability.
|
|
128
|
+
4. How to run the code, including dependency installation.
|
|
129
|
+
5. Next steps if parameters are still needed.
|
|
130
|
+
Return: Markdown string (no JSON).
|
|
131
|
+
"""
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import random
|
|
2
|
+
from typing import Annotated, List, Tuple
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import sympy as sp
|
|
6
|
+
from langchain_core.tools import tool
|
|
7
|
+
from sympy.parsing.sympy_parser import parse_expr, standard_transformations
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@tool(parse_docstring=True)
|
|
11
|
+
def heuristic_feasibility_check(
|
|
12
|
+
constraints: Annotated[List[str], "List of strings like 'x0+x1<=5'"],
|
|
13
|
+
variable_name: Annotated[
|
|
14
|
+
List[str], "List of strings like 'x0', 'x1', etc."
|
|
15
|
+
],
|
|
16
|
+
variable_type: Annotated[
|
|
17
|
+
List[str], "List of strings like 'real', 'integer', 'boolean', etc."
|
|
18
|
+
],
|
|
19
|
+
variable_bounds: Annotated[
|
|
20
|
+
List[List[float]],
|
|
21
|
+
"List of (lower bound, upper bound) tuples for x0, x1, ...'",
|
|
22
|
+
],
|
|
23
|
+
samples: Annotated[int, "Number of random sample. Default 10000"] = 10000,
|
|
24
|
+
) -> Tuple[str]:
|
|
25
|
+
"""
|
|
26
|
+
A tool for checking feasibility of the constraints.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
constraints: list of strings like 'x0 + x1 <= 5', etc.
|
|
30
|
+
variable_name: list of strings containing variable names used in constraint expressions.
|
|
31
|
+
variable_type: list of strings like 'real', 'integer', 'boolean', etc.
|
|
32
|
+
variable_bounds: list of (lower, upper) tuples for x0, x1, etc.
|
|
33
|
+
samples: number of random samples, default value 10000
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
A string indicating whether a feasible solution was found.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
symbols = sp.symbols(variable_name)
|
|
40
|
+
|
|
41
|
+
# Build a dict mapping each name to its Symbol, for parsing
|
|
42
|
+
locals_map = {name: sym for name, sym in zip(variable_name, symbols)}
|
|
43
|
+
|
|
44
|
+
# Parse constraints into Sympy Boolean expressions
|
|
45
|
+
parsed_constraints = []
|
|
46
|
+
try:
|
|
47
|
+
for expr in constraints:
|
|
48
|
+
parsed = parse_expr(
|
|
49
|
+
expr,
|
|
50
|
+
local_dict=locals_map,
|
|
51
|
+
transformations=standard_transformations,
|
|
52
|
+
evaluate=False,
|
|
53
|
+
)
|
|
54
|
+
parsed_constraints.append(parsed)
|
|
55
|
+
except Exception as e:
|
|
56
|
+
return f"Error parsing constraints: {e}"
|
|
57
|
+
|
|
58
|
+
# Sampling loop
|
|
59
|
+
n = len(parsed_constraints)
|
|
60
|
+
funcs = [
|
|
61
|
+
sp.lambdify(symbols, c, modules=["math", "numpy"])
|
|
62
|
+
for c in parsed_constraints
|
|
63
|
+
]
|
|
64
|
+
constraint_satisfied = np.zeros(n, dtype=int)
|
|
65
|
+
for _ in range(samples):
|
|
66
|
+
point = {}
|
|
67
|
+
for i, sym in enumerate(symbols):
|
|
68
|
+
typ = variable_type[i].lower()
|
|
69
|
+
low, high = variable_bounds[i]
|
|
70
|
+
if typ == "integer":
|
|
71
|
+
value = random.randint(int(low), int(high))
|
|
72
|
+
elif typ in ("real", "continuous"):
|
|
73
|
+
value = random.uniform(low, high)
|
|
74
|
+
elif typ in ("boolean", "logical"):
|
|
75
|
+
value = random.choice([False, True])
|
|
76
|
+
else:
|
|
77
|
+
raise ValueError(
|
|
78
|
+
f"Unknown type {variable_type[i]} for variable {variable_name[i]}"
|
|
79
|
+
)
|
|
80
|
+
point[sym] = value
|
|
81
|
+
|
|
82
|
+
# Evaluate all constraints at this point
|
|
83
|
+
try:
|
|
84
|
+
vals = [point[s] for s in symbols]
|
|
85
|
+
cons_satisfaction = [
|
|
86
|
+
bool(np.asarray(f(*vals)).all()) for f in funcs
|
|
87
|
+
]
|
|
88
|
+
if all(cons_satisfaction):
|
|
89
|
+
# Found a feasible point
|
|
90
|
+
readable = {str(k): round(v, 3) for k, v in point.items()}
|
|
91
|
+
return f"Feasible solution found: {readable}"
|
|
92
|
+
else:
|
|
93
|
+
constraint_satisfied += np.array(cons_satisfaction)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
return f"Error evaluating constraint at point {point}: {e}"
|
|
96
|
+
|
|
97
|
+
rates = constraint_satisfied / samples # fraction satisfied per constraint
|
|
98
|
+
order = np.argsort(rates) # lowest (most violated) first
|
|
99
|
+
|
|
100
|
+
lines = []
|
|
101
|
+
for rank, idx in enumerate(order, start=1):
|
|
102
|
+
expr_text = constraints[
|
|
103
|
+
idx
|
|
104
|
+
] # use the original string; easier to read than str(sympy_expr)
|
|
105
|
+
sat = constraint_satisfied[idx]
|
|
106
|
+
lines.append(
|
|
107
|
+
f"[C{idx + 1}] {expr_text} — satisfied {sat:,}/{samples:,} ({sat / samples:.1%}), "
|
|
108
|
+
f"violated {1 - sat / samples:.1%}"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
f"No feasible solution found after {samples:,} samples. Most violated constraints (low→high satisfaction):\n "
|
|
113
|
+
+ "\n ".join(lines)
|
|
114
|
+
)
|