Skip to content

Commit cb88309

Browse files
authored
ci: add code example linting with genvm-linter (#364)
- Add lint-code-examples.py script that extracts Python blocks from MDX files and validates them: runner hash pinning, deprecated API detection, AST lint via genvm-lint, and syntax checks for snippets - Add CI workflow (lint-code-examples.yml) triggered on PRs and pushes to main when pages/ or the lint script change - Fix deprecated APIs across 20 MDX files: gl.get_webpage -> gl.nondet.web.get, gl.eq_principle_strict_eq -> gl.eq_principle.strict_eq, gl.eq_principle_prompt_comparative -> gl.eq_principle.prompt_comparative, gl.eq_principle_prompt_non_comparative -> gl.eq_principle.prompt_non_comparative, gl.exec_prompt -> gl.nondet.exec_prompt - Pin all runner hashes from "test" to specific version
1 parent 831e034 commit cb88309

23 files changed

+541
-78
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Lint Code Examples
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'pages/**/*.mdx'
7+
- 'scripts/lint-code-examples.py'
8+
push:
9+
branches: [main]
10+
paths:
11+
- 'pages/**/*.mdx'
12+
- 'scripts/lint-code-examples.py'
13+
14+
jobs:
15+
lint-code-examples:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: actions/setup-python@v5
20+
with:
21+
python-version: '3.12'
22+
- run: pip install genvm-linter
23+
- run: python scripts/lint-code-examples.py

pages/api-references/genlayer-test.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ assert default_account != other_account
139139
For the following code examples, we'll use a Storage Intelligent Contract as a reference:
140140

141141
```python
142-
# { "Depends": "py-genlayer:test" }
142+
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
143143
144144
from genlayer import *
145145

pages/developers/intelligent-contracts/crafting-prompts.mdx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ When crafting prompts for LLMs, it's important to use a format that clearly and
1515
In the example **Wizard of Coin** contract below, we want the LLM to decide whether the wizard should give the coin to an adventurer.
1616

1717
```python
18-
# { "Depends": "py-genlayer:test" }
18+
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
1919
from genlayer import *
2020

2121
import json
@@ -55,13 +55,14 @@ This result should be perfectly parseable by a JSON parser without errors.
5555
"""
5656

5757
def nondet():
58-
res = gl.exec_prompt(prompt)
59-
res = res.replace("```json", "").replace("```", "")
58+
res = gl.nondet.exec_prompt(prompt)
59+
backticks = "``" + "`"
60+
res = res.replace(backticks + "json", "").replace(backticks, "")
6061
print(res)
6162
dat = json.loads(res)
6263
return dat["give_coin"]
6364

64-
result = gl.eq_principle_strict_eq(nondet)
65+
result = gl.eq_principle.strict_eq(nondet)
6566
assert isinstance(result, bool)
6667
self.have_coin = result
6768

pages/developers/intelligent-contracts/examples/fetch-github-profile.mdx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The FetchGitHubProfile contract demonstrates how to fetch and store GitHub profile content within an intelligent contract. This contract shows how to use the [comparative equivalence principle](/developers/intelligent-contracts/equivalence-principle#comparative-equivalence-principle) to ensure all nodes agree on the same profile content.
44

55
```python
6-
# { "Depends": "py-genlayer:test" }
6+
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
77

88
from genlayer import *
99
import typing
@@ -20,9 +20,10 @@ class FetchGitHubProfile(gl.Contract):
2020
github_profile_url = "https://github.com/"+github_handle
2121

2222
def fetch_github_profile_page_content() -> str:
23-
return gl.get_webpage(github_profile_url, mode="text")
24-
25-
self.github_profile = gl.eq_principle_strict_eq(fetch_github_profile_page_content)
23+
response = gl.nondet.web.get(github_profile_url)
24+
return response.body.decode("utf-8")
25+
26+
self.github_profile = gl.eq_principle.strict_eq(fetch_github_profile_page_content)
2627

2728
@gl.public.view
2829
def show_github_profile(self) -> str:
@@ -35,14 +36,14 @@ class FetchGitHubProfile(gl.Contract):
3536
- **Write Method**:
3637
- `fetch_github_profile(github_handle)` takes a GitHub username and retrieves their profile content.
3738
- Constructs the profile URL using the provided handle.
38-
- Uses `gl.eq_principle_strict_eq()` to ensure all nodes agree on the same profile content.
39+
- Uses `gl.eq_principle.strict_eq()` to ensure all nodes agree on the same profile content.
3940
- **Read Method**:
4041
- `show_github_profile()` returns the stored profile content.
4142

4243
## Key Components
4344

44-
1. **GitHub Integration**: The contract uses `gl.get_webpage()` to fetch content from GitHub profiles.
45-
2. **Deterministic Execution**: `gl.eq_principle_strict_eq()` ensures that all nodes in the network arrive at the same exact content.
45+
1. **GitHub Integration**: The contract uses `gl.nondet.web.get()` to fetch content from GitHub profiles.
46+
2. **Deterministic Execution**: `gl.eq_principle.strict_eq()` ensures that all nodes in the network arrive at the same exact content.
4647
3. **State Management**: The contract maintains a single string state variable that stores the profile content.
4748

4849
## Deploying the Contract

pages/developers/intelligent-contracts/examples/fetch-web-content.mdx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The FetchWebContent contract demonstrates how to fetch and store web content within an intelligent contract. This contract shows how to use the [comparative equivalence principle](/developers/intelligent-contracts/equivalence-principle#comparative-equivalence-principle) to ensure all nodes agree on the same web content.
44

55
```python
6-
# { "Depends": "py-genlayer:test" }
6+
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
77

88
from genlayer import *
99
import typing
@@ -19,9 +19,10 @@ class FetchWebContent(gl.Contract):
1919
def fetch_web_content(self) -> typing.Any:
2020

2121
def fetch_web_url_content() -> str:
22-
return gl.get_webpage("https://example.com/", mode="text")
23-
24-
self.content = gl.eq_principle_strict_eq(fetch_web_url_content)
22+
response = gl.nondet.web.get("https://example.com/")
23+
return response.body.decode("utf-8")
24+
25+
self.content = gl.eq_principle.strict_eq(fetch_web_url_content)
2526

2627
@gl.public.view
2728
def show_content(self) -> str:
@@ -33,15 +34,15 @@ class FetchWebContent(gl.Contract):
3334
- **Initialization**: The `FetchWebContent` class initializes with an empty string in the `content` variable.
3435
- **Write Method**:
3536
- `fetch_web_content()` retrieves content from a web page and stores it.
36-
- It contains an inner function `fetch_web_url_content()` that uses `gl.get_webpage()` to fetch content.
37-
- Uses `gl.eq_principle_strict_eq()` to ensure all nodes agree on the same content.
37+
- It contains an inner function `fetch_web_url_content()` that uses `gl.nondet.web.get()` to fetch content.
38+
- Uses `gl.eq_principle.strict_eq()` to ensure all nodes agree on the same content.
3839
- **Read Method**:
3940
- `show_content()` returns the stored web content.
4041

4142
## Key Components
4243

43-
1. **Web Integration**: The contract uses `gl.get_webpage()` to fetch content from web URLs.
44-
2. **Deterministic Execution**: `gl.eq_principle_strict_eq()` ensures that all nodes in the network arrive at the same exact content.
44+
1. **Web Integration**: The contract uses `gl.nondet.web.get()` to fetch content from web URLs.
45+
2. **Deterministic Execution**: `gl.eq_principle.strict_eq()` ensures that all nodes in the network arrive at the same exact content.
4546
3. **State Management**: The contract maintains a single string state variable that stores the web content.
4647

4748
## Deploying the Contract
@@ -101,7 +102,7 @@ You can monitor the contract's behavior through transaction logs, which will sho
101102

102103
## HTML Mode for Web Content
103104

104-
The `gl.get_webpage()` function supports different modes for retrieving web content. While `mode="text"` returns the plain text content, `mode="html"` allows you to retrieve the complete HTML `<body>` of the webpage.
105+
The `gl.nondet.web.get()` function supports different modes for retrieving web content. While the default mode returns the plain text content, `gl.nondet.web.render()` with `mode="html"` allows you to retrieve the complete HTML `<body>` of the webpage.
105106

106107
Here's an example of using HTML mode:
107108

@@ -115,9 +116,9 @@ class FetchHTMLContent(gl.Contract):
115116
@gl.public.write
116117
def fetch_html_content(self) -> typing.Any:
117118
def fetch_web_url_html() -> str:
118-
return gl.get_webpage("https://example.com/", mode="html")
119-
120-
self.html_content = gl.eq_principle_strict_eq(fetch_web_url_html)
119+
return gl.nondet.web.render("https://example.com/", mode='html')
120+
121+
self.html_content = gl.eq_principle.strict_eq(fetch_web_url_html)
121122

122123
@gl.public.view
123124
def show_html_content(self) -> str:

pages/developers/intelligent-contracts/examples/github-profile-projects.mdx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The GitHubProfilesRepositories contract demonstrates how to fetch GitHub profile data, analyze repository counts, and store profiles of high-contributing developers. This contract shows how to use the [comparative equivalence principle](/developers/intelligent-contracts/equivalence-principle#comparative-equivalence-principle) along with pattern matching to process web content.
44

55
```python
6-
# { "Depends": "py-genlayer:test" }
6+
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
77

88
from genlayer import *
99
import typing
@@ -20,7 +20,8 @@ class GitHubProfilesRepositories(gl.Contract):
2020
github_profile_url = "https://github.com/"+github_handle
2121

2222
def fetch_github_profile_repositories() -> int:
23-
profile_web_page = gl.get_webpage(github_profile_url, mode="text")
23+
response = gl.nondet.web.get(github_profile_url)
24+
profile_web_page = response.body.decode("utf-8")
2425
# Regular expression to find the number between "Repositories" and "Projects"
2526
pattern = r"Repositories\s+(\d+)\s+Projects"
2627

@@ -33,7 +34,7 @@ class GitHubProfilesRepositories(gl.Contract):
3334
else:
3435
return 0
3536

36-
repositories = gl.eq_principle_strict_eq(fetch_github_profile_repositories)
37+
repositories = gl.eq_principle.strict_eq(fetch_github_profile_repositories)
3738

3839
if repositories > 25:
3940
self.github_profiles.append(github_handle)
@@ -55,9 +56,9 @@ class GitHubProfilesRepositories(gl.Contract):
5556

5657
## Key Components
5758

58-
1. **GitHub Integration**: Uses `gl.get_webpage()` to fetch profile content.
59+
1. **GitHub Integration**: Uses `gl.nondet.web.get()` to fetch profile content.
5960
2. **Pattern Matching**: Employs regular expressions to extract repository counts.
60-
3. **Deterministic Execution**: Uses `gl.eq_principle_strict_eq()` to ensure network consensus.
61+
3. **Deterministic Execution**: Uses `gl.eq_principle.strict_eq()` to ensure network consensus.
6162
4. **Conditional Storage**: Only stores profiles meeting specific criteria.
6263

6364
## Deploying the Contract

pages/developers/intelligent-contracts/examples/github-profile-summary.mdx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The GitHubProfilesSummaries contract demonstrates how to fetch GitHub profile data and generate AI-powered summaries of user profiles. This contract shows how to combine web scraping with AI analysis using both [comparative](/developers/intelligent-contracts/equivalence-principle#comparative-equivalence-principle) and [non-comparative](/developers/intelligent-contracts/equivalence-principle#non-comparative-equivalence-principle) equivalence principles.
44

55
```python
6-
# { "Depends": "py-genlayer:test" }
6+
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
77

88
from genlayer import *
99
import typing
@@ -25,17 +25,18 @@ class GitHubProfilesSummaries(gl.Contract):
2525
github_profile_url = "https://github.com/"+github_handle
2626

2727
def fetch_github_profile_summaries() -> str:
28-
return gl.get_webpage(github_profile_url, mode="text")
28+
response = gl.nondet.web.get(github_profile_url)
29+
return response.body.decode("utf-8")
2930

30-
profile_content = gl.eq_principle_strict_eq(fetch_github_profile_summaries)
31+
profile_content = gl.eq_principle.strict_eq(fetch_github_profile_summaries)
3132

3233
task = """Given the web page content of a github profile in HTML format, generate a comprehensive
3334
summary of the profile mentioning the key meta attributes and the GitHub contribution most important metrics"""
3435

3536
criteria = """The summary provided should include different metrics and a summary of a GitHub profile"""
3637

3738
profile_summary = (
38-
gl.eq_principle_prompt_non_comparative(
39+
gl.eq_principle.prompt_non_comparative(
3940
lambda: profile_content,
4041
task=task,
4142
criteria=criteria,
@@ -61,7 +62,7 @@ summary of the profile mentioning the key meta attributes and the GitHub contrib
6162
## Key Components
6263

6364
1. **Data Storage**: Uses `TreeMap` for efficient key-value storage of profile summaries.
64-
2. **Web Fetching**: Uses `gl.get_webpage()` with strict equivalence for deterministic content retrieval.
65+
2. **Web Fetching**: Uses `gl.nondet.web.get()` with strict equivalence for deterministic content retrieval.
6566
3. **AI Analysis**: Uses non-comparative equivalence for generating profile summaries.
6667
4. **Duplicate Prevention**: Includes checks to prevent regenerating existing summaries.
6768

pages/developers/intelligent-contracts/examples/llm-hello-world-non-comparative.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The LlmHelloWorldNonComparative contract demonstrates a simple example of integrating AI capabilities within an intelligent contract without requiring that all the validators execute the full task. They just need to evaluate the leader's response against the specified criteria. This is done by using the [non-comparative equivalence principle](/developers/intelligent-contracts/equivalence-principle#non-comparative-equivalence-principle).
44

55
```python
6-
# { "Depends": "py-genlayer:test" }
6+
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
77

88
from genlayer import *
99
import typing
@@ -17,7 +17,7 @@ class LlmHelloWorldNonComparative(gl.Contract):
1717

1818
@gl.public.write
1919
def set_message(self) -> typing.Any:
20-
self.message = gl.eq_principle_prompt_non_comparative(
20+
self.message = gl.eq_principle.prompt_non_comparative(
2121
lambda: "There is no context, I just want you to answer with truthy value in python (for example: 'yes', 'True', 1)",
2222
task="Answer with truthy value in python (for example: 'yes', 'True', 1)",
2323
criteria="Answer should be a truthy value in python"
@@ -33,7 +33,7 @@ class LlmHelloWorldNonComparative(gl.Contract):
3333
- **Initialization**: The `LlmHelloWorldNonComparative` class initializes with an empty string in the `message` variable.
3434
- **Write Method**:
3535
- `set_message()` uses AI functionality to generate and store a message.
36-
- Uses `gl.eq_principle_prompt_non_comparative()` with three parameters:
36+
- Uses `gl.eq_principle.prompt_non_comparative()` with three parameters:
3737
- A lambda function providing the prompt
3838
- A task description
3939
- Validation criteria for the response
@@ -43,7 +43,7 @@ class LlmHelloWorldNonComparative(gl.Contract):
4343
## Key Components
4444

4545
1. **AI Integration**: The contract uses non-comparative equivalence principle to interact with an AI model.
46-
2. **Deterministic Execution**: `gl.eq_principle_prompt_non_comparative()` ensures that all nodes in the network accept responses that meet the specified criteria.
46+
2. **Deterministic Execution**: `gl.eq_principle.prompt_non_comparative()` ensures that all nodes in the network accept responses that meet the specified criteria.
4747
3. **State Management**: The contract maintains a single string state variable that stores the AI response.
4848

4949
## Deploying the Contract

pages/developers/intelligent-contracts/examples/llm-hello-world.mdx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The LlmHelloWorld contract demonstrates a simple example of integrating AI capabilities within an intelligent contract. This contract shows how to use the [comparative equivalence principle](/developers/intelligent-contracts/equivalence-principle#comparative-equivalence-principle) to call an LLM and store the response in the contract state.
44

55
```python
6-
# { "Depends": "py-genlayer:test" }
6+
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
77

88
from genlayer import *
99
import typing
@@ -20,11 +20,11 @@ class LlmHelloWorld(gl.Contract):
2020

2121
def get_message() -> str:
2222
task = "There is no context, I just want you to answer with a string equal to 'yes'"
23-
result = gl.exec_prompt(task)
23+
result = gl.nondet.exec_prompt(task)
2424
print(result)
2525
return result
2626

27-
self.message = gl.eq_principle_strict_eq(get_message)
27+
self.message = gl.eq_principle.strict_eq(get_message)
2828

2929
@gl.public.view
3030
def get_message(self) -> str:
@@ -37,14 +37,14 @@ class LlmHelloWorld(gl.Contract):
3737
- **Write Method**:
3838
- `set_message()` uses AI functionality to generate and store a message.
3939
- It contains an inner function `get_message()` that prompts an AI model with a simple task.
40-
- Uses `gl.eq_principle_strict_eq()` to ensure deterministic AI responses across the network.
40+
- Uses `gl.eq_principle.strict_eq()` to ensure deterministic AI responses across the network.
4141
- **Read Method**:
4242
- `get_message()` returns the stored message.
4343

4444
## Key Components
4545

46-
1. **AI Integration**: The contract uses `gl.exec_prompt()` to interact with an AI model.
47-
2. **Deterministic Execution**: `gl.eq_principle_strict_eq()` ensures that all nodes in the network arrive at the same exact result.
46+
1. **AI Integration**: The contract uses `gl.nondet.exec_prompt()` to interact with an AI model.
47+
2. **Deterministic Execution**: `gl.eq_principle.strict_eq()` ensures that all nodes in the network arrive at the same exact result.
4848
3. **State Management**: The contract maintains a single string state variable that stores the AI response.
4949

5050
## Deploying the Contract

pages/developers/intelligent-contracts/examples/prediction.mdx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
The Prediction Market contract sets up a scenario to determine the outcome of a football game between two teams. The contract uses the Equivalence Principle to ensure accurate and consistent decision-making based on the game's resolution data.
44

55
```python filename="PredictionMarket" copy
6-
# { "Depends": "py-genlayer:test" }
6+
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
77

88
from genlayer import *
99

@@ -49,7 +49,8 @@ class PredictionMarket(gl.Contract):
4949
return "Already resolved"
5050

5151
def nondet() -> str:
52-
web_data = gl.get_webpage(self.resolution_url, mode="text")
52+
response = gl.nondet.web.get(self.resolution_url)
53+
web_data = response.body.decode("utf-8")
5354
print(web_data)
5455

5556
task = f"""In the following web page, find the winning team in a matchup between the following teams:
@@ -73,11 +74,11 @@ class PredictionMarket(gl.Contract):
7374
your output must be only JSON without any formatting prefix or suffix.
7475
This result should be perfectly parsable by a JSON parser without errors.
7576
"""
76-
result = gl.exec_prompt(task).replace("```json", "").replace("```", "")
77+
result = gl.nondet.exec_prompt(task).replace("```json", "").replace("```", "")
7778
print(result)
7879
return json.dumps(json.loads(result), sort_keys=True)
7980

80-
result_json = json.loads(gl.eq_principle_strict_eq(nondet))
81+
result_json = json.loads(gl.eq_principle.strict_eq(nondet))
8182

8283
if result_json["winner"] > -1:
8384
self.has_resolved = True

0 commit comments

Comments
 (0)