XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/gpt4free

Lazy load pa providers

8b4edf83
hlohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

5 个文件 +159 -9
Modified g4f-go/fetch-python.sh +38 -0
@@ -24,6 +24,14 @@ PBS_BASE="https://github.com/astral-sh/python-build-standalone/releases/download
24 24 PYORG_BASE="https://www.python.org/ftp/python/${PYVER}"
25 25 G4F_SRC="${G4F_SRC:-$HERE/..}" # gpt4free repository root
26 26 G4F_VERSION="${G4F_VERSION:-$(cd "$G4F_SRC" && python3 -c 'import sys;sys.path.insert(0,"g4f");from version import __version__;print(__version__)' 2>/dev/null || echo 0.4.x)}"
27
28 # Minimum versions for deps that have ancient pure-python releases on PyPI.
29 # `pip download --platform <x>` considers py3-none-any wheels valid for any
30 # target, so an old/misbehaving resolver can pick e.g. aiohttp 0.13.1 (2015,
31 # predates async/await) which crashes the embedded CPython 3.14. Pinning
32 # floors here keeps the offline wheel set sane.
33 WHEEL_FLOORS="aiohttp>=3.8"
34
27 35 WORK="$(mktemp -d)"
28 36 trap 'rm -rf "$WORK"' EXIT
29 37
@@ -99,12 +107,17 @@ do_platform() {
99 107 esac
100 108 mkdir -p "$dir/wheels"
101 109 if [ -n "$tag" ]; then
110 # Windows pip can't read process-substitution FDs from its subprocess, so
111 # write the version floors to a real file once per platform.
112 local floors="$dir/wheels.floors"
113 printf '%s\n' "$WHEEL_FLOORS" > "$floors"
102 114 # Full dependency resolution (no --no-deps) so the runtime's offline
103 115 # `pip install g4f` finds every transitive wheel it needs.
104 116 # brotli is optional in g4f and has no cp314 wheel for win_arm64;
105 117 # fall back to fetching everything else if a single dep is unavailable.
106 118 if ! python3 -m pip download \
107 119 -r "$G4F_SRC/requirements-min.txt" \
120 --constraint "$floors" \
108 121 --only-binary=:all: \
109 122 --python-version "$PYVER" --implementation cp --abi "cp$(echo "$PYVER" | tr -d '.')" \
110 123 --platform "$tag" \
@@ -113,6 +126,7 @@ do_platform() {
113 126 grep -v '^brotli$' "$G4F_SRC/requirements-min.txt" > "$dir/req-nobrotli.txt"
114 127 python3 -m pip download \
115 128 -r "$dir/req-nobrotli.txt" \
129 --constraint "$floors" \
116 130 --only-binary=:all: \
117 131 --python-version "$PYVER" --implementation cp --abi "cp$(echo "$PYVER" | tr -d '.')" \
118 132 --platform "$tag" \
@@ -120,6 +134,30 @@ do_platform() {
120 134 echo " WARNING: wheel download for $name failed; runtime will need network on first run" >&2
121 135 }
122 136 fi
137 # Belt-and-braces: drop any wheel whose Requires-Python metadata excludes
138 # our interpreter. The --constraint above prevents this at resolve time;
139 # this catches stale wheels (e.g. copied in from wheels-cache) and covers
140 # ancient pure-python releases like aiohttp 0.13.1.
141 python3 - "$dir/wheels" "$PYVER" <<'PY' || true
142 import glob, os, sys, zipfile
143 from packaging.specifiers import SpecifierSet
144 from packaging.version import Version
145 want_v = Version(sys.argv[2])
146 for whl in glob.glob(os.path.join(sys.argv[1], "*.whl")):
147 try:
148 with zipfile.ZipFile(whl) as z:
149 meta = next((n for n in z.namelist() if n.endswith(".dist-info/METADATA")), None)
150 if not meta:
151 continue
152 txt = z.read(meta).decode("utf-8", "replace")
153 rp = next((l.split(":", 1)[1].strip() for l in txt.splitlines()
154 if l.lower().startswith("requires-python:")), None)
155 if rp and not SpecifierSet(rp).contains(want_v):
156 print(f" removing {os.path.basename(whl)} (Requires-Python {rp} excludes {sys.argv[2]})")
157 os.remove(whl)
158 except Exception as e:
159 print(f" skip check {os.path.basename(whl)}: {e}")
160 PY
123 161 fi
124 162
125 163 # 3) Merge g4f package + wheels so the interpreter is self-contained.
Modified g4f-go/process.go +15 -0
@@ -123,10 +123,25 @@ func extractZip(r io.ReaderAt, size int64, dest string) error {
123 123 }
124 124
125 125 // pythonExecutable returns the launcher (unix) or python.exe (windows) path.
126 //
127 // The embedded archive is laid out as <name>/python-home/<exe>, so after
128 // extraction the interpreter lives at binDir/python-home/python.exe on
129 // windows and binDir/python-home/bin/python on unix. We prefer that location
130 // and fall back to the legacy binDir/python(.exe) layout produced by older
131 // archives, mirroring the shell launcher's logic.
126 132 func pythonExecutable(binDir string) string {
133 home := filepath.Join(binDir, "python-home")
127 134 if runtime.GOOS == "windows" {
135 exe := filepath.Join(home, "python.exe")
136 if fi, err := os.Stat(exe); err == nil && !fi.IsDir() {
137 return exe
138 }
128 139 return filepath.Join(binDir, "python.exe")
129 140 }
141 exe := filepath.Join(home, "bin", "python")
142 if fi, err := os.Stat(exe); err == nil && !fi.IsDir() {
143 return exe
144 }
130 145 return filepath.Join(binDir, "python")
131 146 }
132 147
Modified g4f-go/runtime.go +7 -5
@@ -180,13 +180,15 @@ func installG4F(binDir, exe string, start time.Time) error {
180 180 // pipEnv restricts pip to the embedded runtime so the first run never touches
181 181 // the network. A future `g4f-go install g4f --upgrade` can relax this.
182 182 func pipEnv(binDir string) []string {
183 p := pythonExecutable(binDir)
184 lib := filepath.Join(binDir, "Lib", "site-packages")
185 if strings.Contains(p, "bin/python") {
186 lib = filepath.Join(binDir, "lib", "python3.14", "site-packages")
183 home := pythonHome(binDir)
184 // pbs installs are a full layout: lib/pythonX.Y/site-packages (unix) or
185 // Lib/site-packages (windows) inside the interpreter home.
186 lib := filepath.Join(home, "Lib", "site-packages")
187 if runtime.GOOS != "windows" {
188 lib = filepath.Join(home, "lib", "python3.14", "site-packages")
187 189 }
188 190 return []string{
189 "PYTHONHOME=" + pythonHome(binDir),
191 "PYTHONHOME=" + home,
190 192 "PYTHONNOUSERSITE=1",
191 193 "PYTHONDONTWRITEBYTECODE=1",
192 194 "PYTHONUTF8=1",
Modified g4f/mcp/pa_provider.py +35 -4
@@ -67,7 +67,7 @@ import traceback
67 67 import types
68 68 import builtins as _builtins
69 69 from pathlib import Path
70 from typing import Any, Dict, FrozenSet, List, Optional, Type
70 from typing import Any, Dict, FrozenSet, List, Optional, Tuple, Type
71 71 from .. import debug
72 72
73 73 # ---------------------------------------------------------------------------
@@ -748,7 +748,7 @@ def load_pa_provider(file_path: "str | Path") -> Optional[Type]:
748 748 raise ValueError(f"File must have .pa.py extension: {file_path}")
749 749
750 750 code = file_path.read_text(encoding="utf-8")
751 result = execute_safe_code(code, file_path=file_path)
751 result = execute_safe_code(code, file_path=file_path, timeout=0.1, max_depth=100)
752 752
753 753 if not result.success:
754 754 raise RuntimeError(
@@ -770,7 +770,7 @@ def load_pa_provider(file_path: "str | Path") -> Optional[Type]:
770 770 return None
771 771
772 772
773 def list_pa_providers(directory: "Optional[str | Path]" = None) -> List[Path]:
773 def list_pa_providers(directory: "Optional[str | Path]" = None) -> Tuple[Path, List[Path]]:
774 774 """Return all ``.pa.py`` files found (recursively) in *directory*.
775 775
776 776 Args:
@@ -830,10 +830,39 @@ class PaProviderRegistry:
830 830 if _time_module.monotonic() - self._loaded_at >= self.TTL:
831 831 self.refresh()
832 832
833 def _ensure_index(self) -> None:
834 if not self._entries:
835 self.index()
836
833 837 # ------------------------------------------------------------------
834 838 # Public API
835 839 # ------------------------------------------------------------------
836 840
841 def index(self) -> None:
842 """Re-scan the workspace and reload all ``.pa.py`` providers."""
843 entries: List[tuple] = []
844 directory, pa_paths = list_pa_providers()
845 for pa_path in pa_paths:
846 try:
847 relative_path = pa_path.relative_to(directory).as_posix()
848 provider_id = self._make_id(pa_path)
849 entries.append(
850 (
851 provider_id,
852 None,
853 None,
854 True,
855 None,
856 None,
857 relative_path,
858 )
859 )
860 except Exception as e:
861 debug.error(f"Failed to load PA provider from {pa_path}:", e)
862 pass
863 self._entries = entries
864
865
837 866 def refresh(self) -> None:
838 867 """Re-scan the workspace and reload all ``.pa.py`` providers."""
839 868 entries: List[tuple] = []
@@ -893,9 +922,11 @@ class PaProviderRegistry:
893 922
894 923 def get_provider_class(self, provider_id: str) -> Optional[Type]:
895 924 """Return the provider class for *provider_id*, or ``None``."""
896 self._ensure_fresh()
925 self._ensure_index()
897 926 for e in self._entries:
898 927 if e[0] == provider_id:
928 if e[5] is None:
929 return load_pa_provider(get_workspace_dir() / e[6])
899 930 return e[5]
900 931 return None
901 932
Modified g4f/tools/optimize_request.py +64 -0
@@ -111,6 +111,70 @@ REPLACE_VSC = (
111 111 "- **Skills:** Use `read_file` to load detailed skill instructions when a "
112 112 "task matches a skill's domain (e.g., use `project-setup-info-local` for "
113 113 "scaffolding new projects from scratch, not for adding individual files)."
114 """
115 # ADHD Mode: Escaping the Textbook Answer
116
117 The first three answers to any problem are obvious, safe, and forgettable. The interesting answers live past number three, in the "awkward middle." This skill forces the model there using parallel divergent ideation.
118
119 ## Pre-flight Gate (Cost Check)
120 This process costs ~10 parallel Agent calls (5-10x normal). Run this gate first.
121 1. **Explicit Invocation:** If invoked via `/adhd` or explicit user request, **skip to Phase 1**.
122 2. **Self-Judge:** Abort and answer directly unless ALL three are true:
123 - **Open-ended:** Multiple viable answers exist (no canonical answer).
124 - **High-stakes:** Architecture, public APIs, fuzzy bugs (not a 11pm side-project).
125 - **Open phrasing:** User didn't use words like "quick," "standard," "just," or "textbook."
126 *(If aborted, append: "Run `/adhd <your problem>` for wider exploration.")*
127
128 ## The Loop
129 Strict separation is required; the critic must not strangle the generator.
130
131 ### Phase 1 — Diverge (No Critic)
132 1. Pick 5 cognitive frames from the table below. Bias toward `code`/`design` for technical problems; always include 1 `wild`.
133 2. Spawn 5 **parallel, isolated** Agent/Task calls (one per frame). *Critical invariant: Do NOT serialize or share context between branches.*
134 3. **Agent Instruction:**
135 > DIVERGENT MODE: Generate 6 short, distinct ideas under this frame. The first 3 obvious answers are banned. No evaluation, ranking, or hedging. Push into the awkward middle. Output JSON array only: `[{"text": "...", "rationale": "..."}]`
136
137 ### Phase 2 — Focus (Critic On)
138 1. **Score:** Rate ideas 0-10 on Novelty (0.35 weight), Viability (0.40), Fit (0.25). Flag traps (hidden costs, false economies) with a one-line reason.
139 2. **Cluster:** Group into 3-6 clusters by underlying angle (e.g., "cache-shaped plays").
140 3. **Deepen Top 3:** Spawn 1 Agent call per top idea (excluding traps). **Agent Instruction:**
141 > FOCUS MODE: Sketch how this works (4-8 sentences). Name the load-bearing risk. Name the first concrete coding step. Generate 3-5 child ideas (variations/unlocks). Output JSON only.
142
143 ## Cognitive Frames (Pick 5 per run)
144 | Frame | Vantage Prompt | Tags |
145 |---|---|---|
146 | **Hardware engineer** | Think in latency, memory layout, physical constraints. What does bus topology/cache/timing tell you? | code, wild |
147 | **Regulator** | Audit for compliance/failure modes. What must be provable, traceable, or refusable here? | design, general |
148 | **10-year-old** | Naive but unencumbered. Ignore convention. | general, wild |
149 | **Hostile competitor** | Exploit, fail, or sabotage the obvious solution. Then invert into ideas. | code, design |
150 | **Biology** | Transplant a mechanism from biology (immune systems, cell signaling). Force-fit it. | code, wild |
151 | **Logistics** | Steal from logistics: queues, batching, JIT, hub-and-spoke. Apply literally. | code, design |
152 | **Game design** | What are the loops, rewards, friction, speedrun tricks? Treat the user as a player. | design, general |
153 | **Markets** | Treat as a market. Buyers, sellers, auction, clearing house—what do they look like here? | design, wild |
154 | **Inversion** | Brainstorm how to guarantee NOT X. Then negate each answer back. | code, design, general |
155 | **Extreme: $0, 1hr** | Crudest version that still does the load-bearing thing? | code, general |
156 | **Extreme: ∞ budget, 10yrs** | Maximalist version? | design, wild |
157 | **Remove assumption** | Name the thing everyone treats as fixed. Imagine it is gone. What is possible? | code, design, wild |
158 | **Speedrunner** | Find glitches, skips, out-of-bounds tricks. What is the abusive-but-legal path? | code, wild |
159 | **Ant colony** | No central planner. Many dumb agents, local rules. How does this solve itself emergently? | code, wild |
160 | **3am on-call** | You are woken at 3am when this breaks. What design prevents the page? | code, design |
161
162 ## Output Shape
163 1. **Brief:** 1-2 lines confirming the problem/reframe.
164 2. **Wide Set:** Full pool grouped by cluster (labeled by angle). Short phrases with score chips `[N7 V8 F9]`.
165 3. **Converge:** 2-4 idea shortlist with rationale. Mark non-obvious viable picks with ★. List traps separately with 1-line reasons.
166 4. **Focus:** The 3 deepened branches (sketch, risk, first step, child ideas).
167 5. **Provocation:** 1 wildcard question opening a new direction.
168
169 ## Anti-patterns & Calibration
170 - **No fake divergence:** 10 variations of one assumption isn't breadth; it's decoration.
171 - **No weird-for-weird's-sake:** Always converge with a real opinion. "You decide" is a cop-out.
172 - **Strict Isolation:** Simulating parallel branches in one context just creates a wider single thought. Use separate Agent contexts.
173 - **Structure over prose:** Cluster, label, and score. Walls of text are useless.
174 - **Calibrate:** Scale ideas to stakes (3x4 for quick tasks, 5x8 for strategy). Stop diverging when candidates repeat shapes.
175
176 *(Note: Use companion CLI `npm install -g adhd-agent` for batch/outside-Claude runs).*
177 """
114 178 )
115 179
116 180