Own plugin via Speckle, Revit, and GPT Chat

Niko G.
9 min readOct 18, 2023

--

Where to begin?

Have you ever thought about creating your own software? If you work in BIM, the answer would be “yes”. Your reasons might vary, from idealistic and ambitious projects that could make you an invaluable asset, to simple and routine ones to alleviate daily mundane tasks. In any case, both tasks require knowledge, at least fundamental, like understanding a programming language (I’m not specifying which one since it largely depends on the software you’re working with).

I was no exception. I had an idea to develop a program using AI. Yes, of course, I could have written it myself, but I became incredibly curious if one could write software with the help of AI without knowing how to code. This series of articles is dedicated to this very journey. Step by step, I will explore AI capabilities and write the code that will form the foundation of my future software. Enough talking, let’s embark on this long and challenging journey!

Preparation

Every idea has to undergo a “preparation” phase. We need to clearly outline what we want to achieve in the end, even if it’s just in broad strokes. Once we have this “roadmap”, we can start thinking about the tools.

So here’s the Idea.

The idea is for me, as a user, to be able to monitor the data coming out of a Revit model.

As you can see, the idea is straightforward. That’s exactly what we need. “Simple” doesn’t mean “easy”. We’ll use this straightforward idea as a modular construct. That is, 1 idea equals 1 module, allowing us to enhance functionality without complicating the code.

Chat-GPT

Version: GPT-4

Subscription: Paid ($20 per month)

Plugins: AskTheCode, DALLE-3

The core idea of this post revolves around AI. The choice of this particular AI is deliberate. Open-AI offers a plethora of diverse features that we will be actively using. It’s worth noting that I’ll be using the paid version. This decision is driven by the desire to utilize the more advanced language model, GPT-4, and the ability to use plugins.

Revit

I will be using Revit 2023, but the version isn’t critically important. However, I still find it essential to mention the software version. We will only be exporting a model from Revit. That will be the extent of our interaction with this software.

Speckle

This solution is essentially the “core” or “database” for our software. Choose whichever term you prefer; the meaning doesn’t change much either way. In essence, this solution will gather information about the models and store them. This means we no longer need to search for a software or storage solution. For more details on Speckle, you can read about it here. Now, let’s not dwell on this and move on.

Python

Choosing a programming language seems quite clear-cut to me. There are three main reasons:

  1. Simple syntax (very forgiving)
  2. Abundance of information and resources
  3. Speed of development

You can argue with me on each of these points, but what cannot be denied is that Python excels in data processing and its language simplicity is top-notch. As an added note, did I learn Python for nothing? xD))

Schema

A schematic representation is crucial. Here, I’ve decided to determine the starting point when it comes to coding. And every plan needs a blueprint. This will allow us to view the project from a slightly different perspective and help in organizing the interrelation of the segments. Here’s what I’ve come up with:

Now, let’s delve deeper. I’ve tried to structure the blueprint of the program itself. Outlining what I expect from it and how we’ll achieve this:

Such diagrams assist me in clearly comprehending what I wish to accomplish. But I’ve detailed it extensively as I roughly understand how my program will be constructed. Let’s simplify this to the most basic level for everyone to understand.

That’s better. I believe the idea is now clear to everyone. It’s as follows:

1. You launch the program.
It’s like turning on the TV or radio.

2. The program connects to an online service.
Similar to how your phone connects to the internet to check for new messages.

3. The program “introduces” itself to the service.
This is analogous to when you log into a website using your username and password.

4. The program requests specific information from the service.
When you search for a video on YouTube: you specify which video you want to watch.

5. The program receives this information.
This is similar to when a video starts playing after you’ve selected it.

6. The program displays this information to you.
Like when you read a text message or view photos on your phone’s screen.

The entire process can be likened to searching and watching a video on YouTube or reading news online. You choose what you want to know, and the program shows it to you.

Starting Up

Let’s begin by uploading our project directly to Speckle. I’ve chosen a standard project for this purpose and uploaded it to the Speckle cloud.

With that, our interaction with Revit is complete. From now on, all we’ll do is fetch data from Speckle. This is what we’ll be requesting from the AI. Let’s get started. Here’s the query we made to the AI:

And here’s the response I received:

import requests

# 1. Setup
SPECKLE_BASE_URL = 'https://your-speckle-server-url.com'
STREAM_ID = 'your-stream-id'
API_TOKEN = 'your-api-token'

# 2. Authentication
headers = {
'Authorization': f'Bearer {API_TOKEN}',
'Content-Type': 'application/json'
}

# 3. Fetch Data
# Fetching branch details
branch_response = requests.get(f'{SPECKLE_BASE_URL}/streams/{STREAM_ID}/branches', headers=headers)
branches = branch_response.json()

for branch in branches:
branch_name = branch['name']
print(f"Branch Name: {branch_name}")

# Fetching objects in the branch to get data uploaded and number of elements
objects_response = requests.get(f'{SPECKLE_BASE_URL}/streams/{STREAM_ID}/branches/{branch_name}/objects', headers=headers)
objects_data = objects_response.json()

data_uploaded = [obj['data'] for obj in objects_data]
number_of_elements = len(data_uploaded)

print(f"Data Uploaded: {data_uploaded}")
print(f"Number of Elements: {number_of_elements}")

And now, please pay attention to the lines below. You need to input your own information here:

SPECKLE_BASE_URL = 'https://your-speckle-server-url.com'
STREAM_ID = 'your-stream-id'
API_TOKEN = 'your-api-token'

Alright, first run. We’ve entered our data and launched the script. And what do we get?

The script doesn’t work. Honestly, that was expected. Now, I’ll copy the issue and send it to the GPT chat to get some advice on what modifications I should make to the code. Let’s try.

import requests  

# 1. Setup
SPECKLE_BASE_URL = 'https://speckle.xyz/'
STREAM_ID = 'your ID'
API_TOKEN = 'your token'

# Authentication
headers = {
'Authorization': f'Bearer {API_TOKEN}',
'Content-Type': 'application/json'
}

# Fetch Data
branch_response = requests.get(
f'{SPECKLE_BASE_URL}/streams/{STREAM_ID}/branches', headers=headers)

# Check if the response was successful
if branch_response.status_code == 200:
try:
branches = branch_response.json()

for branch in branches:
branch_name = branch['name']
print(f"Branch Name: {branch_name}")

# Fetching objects in the branch to get data uploaded and number of elements
objects_response = requests.get(
f'{SPECKLE_BASE_URL}/streams/{STREAM_ID}/branches/{branch_name}/objects',
headers=headers)
objects_data = objects_response.json()

data_uploaded = [obj['data'] for obj in objects_data]
number_of_elements = len(data_uploaded)

print(f"Data Uploaded: {data_uploaded}")
print(f"Number of Elements: {number_of_elements}")
except requests.exceptions.JSONDecodeError:
print("Received an invalid JSON response.")
else:
print(f"Error {branch_response.status_code}: {branch_response.text}")

Well, it seems to work, but not the way we want it to. Let me reiterate, we want GPT to figure things out on its own. Therefore, we’ll continue to provide hints based on our knowledge. For instance, if you look through the documentation on the website, you can find examples. This, example:

As you can see, the library specklepy is being used. I think we should mention to GPT that such a library exists; it should help. Here's what resulted:

from specklepy.api.client import SpeckleClient  
from specklepy.api.credentials import get_default_account, get_local_accounts

# 1. Setup
SPECKLE_BASE_URL = 'https://speckle.xyz/'
STREAM_ID = 'your stream'

# 2. Authentication
account = get_default_account()
if not account:
accounts = get_local_accounts()
if not accounts:
raise Exception("No local accounts found. Please set one up!")
account = accounts[0]

client = SpeckleClient(host=account.serverInfo.url)
client.authenticate(token=account.token)

# 3. Fetch Data
stream = client.stream.get(id=STREAM_ID)
branches = client.branch.list(stream_id=STREAM_ID)

for branch in branches:
branch_name = branch.name
print(f"Branch Name: {branch_name}")

# Fetching objects in the branch to get data uploaded and number of elements
objects = client.object.list(stream_id=STREAM_ID, branch_name=branch_name)
data_uploaded = [obj.data for obj in objects]
number_of_elements = len(data_uploaded)

print(f"Data Uploaded: {data_uploaded}")
print(f"Number of Elements: {number_of_elements}")

And… we got a semi-functional code. Here’s what I got:

We got the name but didn’t receive the rest of the information. Well, that’s okay. It’s a start, and that’s great. Let’s report the error to the AI and check the updated code.

from specklepy.api.client import SpeckleClient  
from specklepy.api.credentials import get_default_account, get_local_accounts

# 1. Setup
SPECKLE_BASE_URL = 'https://speckle.xyz/'
STREAM_ID = 'your stream'

# 2. Authentication
account = get_default_account()
if not account:
accounts = get_local_accounts()
if not accounts:
raise Exception("No local accounts found. Please set one up!")
account = accounts[0]

client = SpeckleClient(host=account.serverInfo.url)
client.authenticate_with_token(account.token)

# 3. Fetch Data
stream = client.stream.get(id=STREAM_ID)
branches = client.branch.list(stream_id=STREAM_ID)

all_commits = client.commit.list(stream_id=STREAM_ID)

for branch in branches:
branch_name = branch.name
print(f"Branch Name: {branch_name}")

# Filter commits by the branch name
branch_commits = [commit for commit in all_commits if
commit.branchName == branch_name]

for commit in branch_commits:
print(f"Commit ID: {commit.id}")
print(f"Message: {commit.message}")
print(f"Author: {commit.authorName}")
print(f"Date: {commit.createdAt}")
print("------")

I want to mention right away that the code looks plausible. Moreover, it works. Here’s the result:

It returned the naming of all commits in the branch. That’s more than I expected. However, one thing was overlooked: the number of items. Let’s request to count the number of items in the commit for me:

from specklepy.api.client import SpeckleClient  
from specklepy.api.credentials import get_default_account
from specklepy.transports.server import ServerTransport
from specklepy.api import operations

# 1. Setup
SPECKLE_BASE_URL = 'https://speckle.xyz/'
STREAM_ID = 'your stram'

# Create and authenticate the client
client = SpeckleClient(host=SPECKLE_BASE_URL)
account = get_default_account()
client.authenticate_with_account(account)

# Fetch Data
stream = client.stream.get(id=STREAM_ID)
branches = client.branch.list(stream_id=STREAM_ID)

all_commits = client.commit.list(stream_id=STREAM_ID)

for branch in branches:
branch_name = branch.name
print(f"Branch Name: {branch_name}")

# Filter commits by the branch name
branch_commits = [commit for commit in all_commits if
commit.branchName == branch_name]

for commit in branch_commits:
print(f"Commit ID: {commit.id}")
print(f"Message: {commit.message}")
print(f"Author: {commit.authorName}")
print(f"Date: {commit.createdAt}")

# Create an authenticated server transport from the client and get the commit object
transport = ServerTransport(client=client, stream_id=STREAM_ID)
res = operations.receive(commit.referencedObject, transport)

# Extract data
upload_date = commit.createdAt
file_name = commit.message
object_count = res.totalChildrenCount

# Print the data
print(f"Upload Date: {upload_date}")
print(f"File Name: {file_name}")
print(f"Number of Elements: {object_count}")

print("------")

Everything works and it’s all sorted out. Here’s what we got in the console:

As you can see, the data is calculated for each commit I made. This means the code works as intended!

And of course, I couldn’t leave you without a video. So, if you’re interested, you can watch a video where I do almost the same thing step by step and on camera.

What’s Next?

Well, we’ve taken the first step. This code can fetch data directly from Speckle. As a preliminary conclusion, I can say that AI can assist in such tasks. But this is just the beginning. Next, I’ll be integrating a “database” here, and we’ll track changes in the model based on that. I hope that, without using my own skills and relying solely on AI, I can develop a program worth noting. Well, time will tell what comes of it.

By the way, almost all the images I’ve generated were with the help of AI.

--

--