curl-programming-lang 1.3.0__tar.gz → 1.4.0__tar.gz
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.
- {curl_programming_lang-1.3.0/curl_programming_lang.egg-info → curl_programming_lang-1.4.0}/PKG-INFO +1 -1
- curl_programming_lang-1.4.0/ai_module.py +159 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0/curl_programming_lang.egg-info}/PKG-INFO +1 -1
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/pyproject.toml +1 -1
- curl_programming_lang-1.3.0/ai_module.py +0 -83
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/LICENSE +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/README.md +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/curl_programming_lang.egg-info/SOURCES.txt +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/curl_programming_lang.egg-info/dependency_links.txt +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/curl_programming_lang.egg-info/entry_points.txt +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/curl_programming_lang.egg-info/top_level.txt +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/errors.py +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/interpreter.py +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/lexer.py +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/main.py +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/parser.py +0 -0
- {curl_programming_lang-1.3.0 → curl_programming_lang-1.4.0}/setup.cfg +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import urllib.request
|
|
4
|
+
import urllib.error
|
|
5
|
+
|
|
6
|
+
_DEFAULT_SYSTEM = (
|
|
7
|
+
"You are Curl-Bot, an AI assistant built into the Curl programming language. "
|
|
8
|
+
"Curl is an open-source language created by Ritvik Gautam that runs on Python. "
|
|
9
|
+
"You are helpful, concise, and friendly."
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
# Auto-compact after this many user+assistant message pairs in history
|
|
13
|
+
_COMPACT_THRESHOLD = 20
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CurlAIModule:
|
|
17
|
+
"""
|
|
18
|
+
AI standard library for Curl.
|
|
19
|
+
|
|
20
|
+
Configuration via environment variables:
|
|
21
|
+
CURL_AI_KEY or OPENAI_API_KEY — API key (not needed for local models)
|
|
22
|
+
CURL_AI_BASE_URL — base URL (default: https://api.openai.com/v1)
|
|
23
|
+
set to http://localhost:11434/v1 for Ollama
|
|
24
|
+
CURL_AI_MODEL — model name (default: gpt-4o-mini)
|
|
25
|
+
|
|
26
|
+
Usage in Curl:
|
|
27
|
+
import{"ai", ai}\
|
|
28
|
+
ai.context{"You are a pirate who only speaks in rhymes."}\
|
|
29
|
+
var{answer, ai.ask{"What is the capital of France?"}}\
|
|
30
|
+
pcType{var{answer}}\
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
self._system = _DEFAULT_SYSTEM
|
|
35
|
+
self._history = [] # list of {"role": ..., "content": ...}
|
|
36
|
+
|
|
37
|
+
def context(self, prompt):
|
|
38
|
+
"""Set a persistent system prompt (persona/context) for all subsequent AI calls."""
|
|
39
|
+
self._system = str(prompt)
|
|
40
|
+
self._history = [] # reset history when context changes
|
|
41
|
+
return ""
|
|
42
|
+
|
|
43
|
+
def reset(self, _=""):
|
|
44
|
+
"""Clear conversation history (start a fresh chat while keeping context)."""
|
|
45
|
+
self._history = []
|
|
46
|
+
return ""
|
|
47
|
+
|
|
48
|
+
def _raw_request(self, messages):
|
|
49
|
+
"""Send a messages list to the API and return the reply string."""
|
|
50
|
+
api_key = os.environ.get("CURL_AI_KEY") or os.environ.get("OPENAI_API_KEY", "")
|
|
51
|
+
base_url = os.environ.get("CURL_AI_BASE_URL", "https://api.openai.com/v1").rstrip("/")
|
|
52
|
+
model = os.environ.get("CURL_AI_MODEL", "gpt-4o-mini")
|
|
53
|
+
|
|
54
|
+
if not api_key and "openai.com" in base_url:
|
|
55
|
+
raise RuntimeError(
|
|
56
|
+
"No API key found. Set CURL_AI_KEY (or OPENAI_API_KEY) in your environment.\n"
|
|
57
|
+
"For a free local model, install Ollama (https://ollama.com), run a model, then set:\n"
|
|
58
|
+
" export CURL_AI_BASE_URL=http://localhost:11434/v1\n"
|
|
59
|
+
" export CURL_AI_MODEL=llama3.2"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
payload = json.dumps({
|
|
63
|
+
"model": model,
|
|
64
|
+
"messages": messages,
|
|
65
|
+
"temperature": 0.7,
|
|
66
|
+
}).encode()
|
|
67
|
+
|
|
68
|
+
headers = {"Content-Type": "application/json"}
|
|
69
|
+
if api_key:
|
|
70
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
71
|
+
|
|
72
|
+
req = urllib.request.Request(
|
|
73
|
+
f"{base_url}/chat/completions",
|
|
74
|
+
data=payload,
|
|
75
|
+
headers=headers,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
with urllib.request.urlopen(req, timeout=60) as resp:
|
|
80
|
+
result = json.loads(resp.read())
|
|
81
|
+
return result["choices"][0]["message"]["content"].strip()
|
|
82
|
+
except urllib.error.HTTPError as e:
|
|
83
|
+
body = e.read().decode(errors="ignore")
|
|
84
|
+
raise RuntimeError(f"AI request failed ({e.code}): {body}")
|
|
85
|
+
except urllib.error.URLError as e:
|
|
86
|
+
raise RuntimeError(f"AI connection failed: {e.reason}")
|
|
87
|
+
|
|
88
|
+
def _compact_history(self):
|
|
89
|
+
"""Summarize old history into a single assistant message to stay within limits."""
|
|
90
|
+
summary_messages = [
|
|
91
|
+
{"role": "system", "content": self._system},
|
|
92
|
+
{
|
|
93
|
+
"role": "user",
|
|
94
|
+
"content": (
|
|
95
|
+
"Summarize the following conversation history into 3-5 bullet points "
|
|
96
|
+
"so it can be used as context going forward. Be concise.\n\n"
|
|
97
|
+
+ "\n".join(
|
|
98
|
+
f"{m['role'].upper()}: {m['content']}" for m in self._history
|
|
99
|
+
)
|
|
100
|
+
),
|
|
101
|
+
},
|
|
102
|
+
]
|
|
103
|
+
summary = self._raw_request(summary_messages)
|
|
104
|
+
self._history = [{"role": "assistant", "content": f"[Previous conversation summary]\n{summary}"}]
|
|
105
|
+
|
|
106
|
+
def _request(self, prompt):
|
|
107
|
+
"""Send prompt with history, auto-compact when history is too long."""
|
|
108
|
+
if len(self._history) >= _COMPACT_THRESHOLD:
|
|
109
|
+
self._compact_history()
|
|
110
|
+
|
|
111
|
+
self._history.append({"role": "user", "content": str(prompt)})
|
|
112
|
+
|
|
113
|
+
messages = [{"role": "system", "content": self._system}] + self._history
|
|
114
|
+
reply = self._raw_request(messages)
|
|
115
|
+
|
|
116
|
+
self._history.append({"role": "assistant", "content": reply})
|
|
117
|
+
return reply
|
|
118
|
+
|
|
119
|
+
def ask(self, prompt):
|
|
120
|
+
"""Send a prompt (with conversation history) and return the response."""
|
|
121
|
+
return self._request(prompt)
|
|
122
|
+
|
|
123
|
+
def summarize(self, text):
|
|
124
|
+
"""Summarize text in 2-3 sentences (no history tracking)."""
|
|
125
|
+
messages = [
|
|
126
|
+
{"role": "system", "content": self._system},
|
|
127
|
+
{"role": "user", "content": f"Summarize the following in 2-3 sentences:\n\n{text}"},
|
|
128
|
+
]
|
|
129
|
+
return self._raw_request(messages)
|
|
130
|
+
|
|
131
|
+
def analyze(self, text):
|
|
132
|
+
"""Analyze text and return key insights (no history tracking)."""
|
|
133
|
+
messages = [
|
|
134
|
+
{"role": "system", "content": self._system},
|
|
135
|
+
{"role": "user", "content": f"Analyze the following and provide key insights:\n\n{text}"},
|
|
136
|
+
]
|
|
137
|
+
return self._raw_request(messages)
|
|
138
|
+
|
|
139
|
+
def sentiment(self, text):
|
|
140
|
+
"""Return the sentiment of text: positive, negative, or neutral (no history tracking)."""
|
|
141
|
+
messages = [
|
|
142
|
+
{"role": "system", "content": self._system},
|
|
143
|
+
{
|
|
144
|
+
"role": "user",
|
|
145
|
+
"content": (
|
|
146
|
+
"What is the sentiment of this text? "
|
|
147
|
+
"Reply with only one word: positive, negative, or neutral.\n\n" + text
|
|
148
|
+
),
|
|
149
|
+
},
|
|
150
|
+
]
|
|
151
|
+
return self._raw_request(messages)
|
|
152
|
+
|
|
153
|
+
def translate(self, text):
|
|
154
|
+
"""Translate text — include the target language in the text itself (no history tracking)."""
|
|
155
|
+
messages = [
|
|
156
|
+
{"role": "system", "content": self._system},
|
|
157
|
+
{"role": "user", "content": f"Translate the following:\n\n{text}"},
|
|
158
|
+
]
|
|
159
|
+
return self._raw_request(messages)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "curl-programming-lang"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.4.0"
|
|
8
8
|
description = "Curl is an open-source programming language built on Python technology"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.7"
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
import urllib.request
|
|
4
|
-
import urllib.error
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class CurlAIModule:
|
|
8
|
-
"""
|
|
9
|
-
AI standard library for Curl.
|
|
10
|
-
|
|
11
|
-
Configuration via environment variables:
|
|
12
|
-
CURL_AI_KEY or OPENAI_API_KEY — API key (not needed for local models)
|
|
13
|
-
CURL_AI_BASE_URL — base URL (default: https://api.openai.com/v1)
|
|
14
|
-
set to http://localhost:11434/v1 for Ollama
|
|
15
|
-
CURL_AI_MODEL — model name (default: gpt-4o-mini)
|
|
16
|
-
|
|
17
|
-
Usage in Curl:
|
|
18
|
-
import{"ai", ai}\
|
|
19
|
-
var{answer, ai.ask{"What is the capital of France?"}}\
|
|
20
|
-
pcType{var{answer}}\
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
def _request(self, prompt):
|
|
24
|
-
api_key = os.environ.get("CURL_AI_KEY") or os.environ.get("OPENAI_API_KEY", "")
|
|
25
|
-
base_url = os.environ.get("CURL_AI_BASE_URL", "https://api.openai.com/v1").rstrip("/")
|
|
26
|
-
model = os.environ.get("CURL_AI_MODEL", "gpt-4o-mini")
|
|
27
|
-
|
|
28
|
-
if not api_key and "openai.com" in base_url:
|
|
29
|
-
raise RuntimeError(
|
|
30
|
-
"No API key found. Set CURL_AI_KEY (or OPENAI_API_KEY) in your environment.\n"
|
|
31
|
-
"For a free local model, install Ollama (https://ollama.com), run a model, then set:\n"
|
|
32
|
-
" export CURL_AI_BASE_URL=http://localhost:11434/v1\n"
|
|
33
|
-
" export CURL_AI_MODEL=llama3.2"
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
payload = json.dumps({
|
|
37
|
-
"model": model,
|
|
38
|
-
"messages": [{"role": "user", "content": str(prompt)}],
|
|
39
|
-
"temperature": 0.7,
|
|
40
|
-
}).encode()
|
|
41
|
-
|
|
42
|
-
headers = {"Content-Type": "application/json"}
|
|
43
|
-
if api_key:
|
|
44
|
-
headers["Authorization"] = f"Bearer {api_key}"
|
|
45
|
-
|
|
46
|
-
req = urllib.request.Request(
|
|
47
|
-
f"{base_url}/chat/completions",
|
|
48
|
-
data=payload,
|
|
49
|
-
headers=headers,
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
try:
|
|
53
|
-
with urllib.request.urlopen(req, timeout=60) as resp:
|
|
54
|
-
result = json.loads(resp.read())
|
|
55
|
-
return result["choices"][0]["message"]["content"].strip()
|
|
56
|
-
except urllib.error.HTTPError as e:
|
|
57
|
-
body = e.read().decode(errors="ignore")
|
|
58
|
-
raise RuntimeError(f"AI request failed ({e.code}): {body}")
|
|
59
|
-
except urllib.error.URLError as e:
|
|
60
|
-
raise RuntimeError(f"AI connection failed: {e.reason}")
|
|
61
|
-
|
|
62
|
-
def ask(self, prompt):
|
|
63
|
-
"""Send a prompt and return the response as a string."""
|
|
64
|
-
return self._request(prompt)
|
|
65
|
-
|
|
66
|
-
def summarize(self, text):
|
|
67
|
-
"""Summarize text in 2-3 sentences."""
|
|
68
|
-
return self._request(f"Summarize the following in 2-3 sentences:\n\n{text}")
|
|
69
|
-
|
|
70
|
-
def analyze(self, text):
|
|
71
|
-
"""Analyze text and return key insights."""
|
|
72
|
-
return self._request(f"Analyze the following and provide key insights:\n\n{text}")
|
|
73
|
-
|
|
74
|
-
def sentiment(self, text):
|
|
75
|
-
"""Return the sentiment of text: positive, negative, or neutral."""
|
|
76
|
-
return self._request(
|
|
77
|
-
f"What is the sentiment of this text? "
|
|
78
|
-
f"Reply with only one word: positive, negative, or neutral.\n\n{text}"
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
def translate(self, text):
|
|
82
|
-
"""Translate text — include the target language in the text itself."""
|
|
83
|
-
return self._request(f"Translate the following:\n\n{text}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|